mirror of
https://github.com/falcosecurity/falco.git
synced 2026-03-20 11:42:06 +00:00
Compare commits
59 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1d9188a316 | ||
|
|
5cc102545f | ||
|
|
2801c62666 | ||
|
|
c6cffc1f48 | ||
|
|
4894c93d5e | ||
|
|
c8703b88bf | ||
|
|
cebec11552 | ||
|
|
61bfd5a158 | ||
|
|
81de65eb69 | ||
|
|
bc9a2f38e1 | ||
|
|
c188f4a731 | ||
|
|
ca04145590 | ||
|
|
511a9fa97f | ||
|
|
7b8f67fdbd | ||
|
|
6e36afdba3 | ||
|
|
9ea195a0b7 | ||
|
|
47fa7d53c4 | ||
|
|
0a33f555eb | ||
|
|
38f524d1dd | ||
|
|
388de27398 | ||
|
|
69d2fa76ff | ||
|
|
39e6d21449 | ||
|
|
3418ed64aa | ||
|
|
d07f18ad05 | ||
|
|
4af705c15d | ||
|
|
469749a2b5 | ||
|
|
3355d0d215 | ||
|
|
b74d60289c | ||
|
|
e4aa646146 | ||
|
|
a4b3af29ae | ||
|
|
17685eaa3c | ||
|
|
b75166ff60 | ||
|
|
2f3669b962 | ||
|
|
1bcac6f251 | ||
|
|
57c62ba6a7 | ||
|
|
6451a55d82 | ||
|
|
0ff220de1e | ||
|
|
78fa43708b | ||
|
|
99d4a7d5c5 | ||
|
|
7f4d5396c2 | ||
|
|
aa8edadf68 | ||
|
|
6ecc691c68 | ||
|
|
4d61f1c739 | ||
|
|
7b70f3c2ef | ||
|
|
8371d1955a | ||
|
|
270c3fa910 | ||
|
|
0a2eab3f19 | ||
|
|
ac2a9a35cb | ||
|
|
85aa337b63 | ||
|
|
d1211ecca8 | ||
|
|
f567f2f7f7 | ||
|
|
ab615c36ad | ||
|
|
60c322a73d | ||
|
|
f12210325f | ||
|
|
682e53f5b5 | ||
|
|
6e8352e847 | ||
|
|
c512784503 | ||
|
|
b0942f8774 | ||
|
|
8b56360f8c |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -11,8 +11,6 @@ test/.phoronix-test-suite
|
||||
test/results*.json.*
|
||||
test/build
|
||||
|
||||
userspace/falco/lua/re.lua
|
||||
userspace/falco/lua/lpeg.so
|
||||
userspace/engine/lua/lyaml
|
||||
userspace/engine/lua/lyaml.lua
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
std = "min"
|
||||
cache = true
|
||||
include_files = {
|
||||
"userspace/falco/lua/*.lua",
|
||||
"userspace/engine/lua/*.lua",
|
||||
"userspace/engine/lua/lyaml/*.lua",
|
||||
"*.luacheckrc"
|
||||
|
||||
@@ -1,13 +1,5 @@
|
||||
# Change Log
|
||||
|
||||
## v0.26.2
|
||||
|
||||
Released on 2020-11-10
|
||||
|
||||
### Major Changes
|
||||
|
||||
* update: DRIVERS_REPO now defaults to https://download.falco.org/driver [[#1460](https://github.com/falcosecurity/falco/pull/1460)] - [@leodido](https://github.com/leodido)
|
||||
|
||||
## v0.26.1
|
||||
|
||||
Released on 2020-10-01
|
||||
|
||||
@@ -60,7 +60,7 @@ if(MUSL_OPTIMIZED_BUILD)
|
||||
set(MUSL_FLAGS "-static -Os")
|
||||
endif()
|
||||
|
||||
set(CMAKE_COMMON_FLAGS "-Wall -ggdb ${DRAIOS_FEATURE_FLAGS} ${MINIMAL_BUILD_FLAGS} ${MUSL_FLAGS}")
|
||||
set(CMAKE_COMMON_FLAGS "-Wall -pg -ggdb ${DRAIOS_FEATURE_FLAGS} ${MINIMAL_BUILD_FLAGS} ${MUSL_FLAGS}")
|
||||
|
||||
if(BUILD_WARNINGS_AS_ERRORS)
|
||||
set(CMAKE_SUPPRESSED_WARNINGS
|
||||
@@ -83,7 +83,7 @@ include(GetFalcoVersion)
|
||||
set(PACKAGE_NAME "falco")
|
||||
set(PROBE_NAME "falco")
|
||||
set(PROBE_DEVICE_NAME "falco")
|
||||
set(DRIVERS_REPO "https://download.falco.org/driver")
|
||||
set(DRIVERS_REPO "https://dl.bintray.com/falcosecurity/driver")
|
||||
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
|
||||
set(CMAKE_INSTALL_PREFIX
|
||||
/usr
|
||||
@@ -123,8 +123,8 @@ set(B64_INCLUDE "${B64_SRC}/include")
|
||||
set(B64_LIB "${B64_SRC}/src/libb64.a")
|
||||
ExternalProject_Add(
|
||||
b64
|
||||
URL "https://github.com/libb64/libb64/archive/v1.2.1.zip"
|
||||
URL_HASH "SHA256=665134c2b600098a7ebd3d00b6a866cb34909a6d48e0e37a0eda226a4ad2638a"
|
||||
URL "https://github.com/libb64/libb64/archive/ce864b17ea0e24a91e77c7dd3eb2d1ac4175b3f0.tar.gz"
|
||||
URL_HASH "SHA256=d07173e66f435e5c77dbf81bd9313f8d0e4a3b4edd4105a62f4f8132ba932811"
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ${CMD_MAKE}
|
||||
BUILD_IN_SOURCE 1
|
||||
|
||||
10
RELEASE.md
10
RELEASE.md
@@ -90,6 +90,16 @@ Now assume `x.y.z` is the new version.
|
||||
|
||||
- Finally, publish the release!
|
||||
|
||||
### 3. Update the meeting notes
|
||||
|
||||
For each release we archive the meeting notes in git for historical purposes.
|
||||
|
||||
- The notes from the Falco meetings can be [found here](https://hackmd.io/6sEAlInlSaGnLz2FnFz21A).
|
||||
- Note: There may be other notes from working groups that can optionally be added as well as needed.
|
||||
- Add the entire content of the document to a new file in [github.com/falcosecurity/community/tree/master/meeting-notes](https://github.com/falcosecurity/community/tree/master/meeting-notes) as a new file labeled `release-x.y.z.md`
|
||||
- Open up a pull request with the new change.
|
||||
|
||||
|
||||
## Post-Release tasks
|
||||
|
||||
Announce the new release to the world!
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -u -o pipefail
|
||||
|
||||
BUILD_DIR=${BUILD_DIR:-/build}
|
||||
SOURCE_DIR=${SOURCE_DIR:-/source}
|
||||
SKIP_PACKAGES_TESTS=${SKIP_PACKAGES_TESTS:-false}
|
||||
@@ -9,6 +7,9 @@ SKIP_PACKAGES_TESTS=${SKIP_PACKAGES_TESTS:-false}
|
||||
CMD=${1:-test}
|
||||
shift
|
||||
|
||||
# Stop the execution if a command in the pipeline has an error, from now on
|
||||
set -e -u -o pipefail
|
||||
|
||||
# build type can be "debug" or "release", fallbacks to "release" by default
|
||||
BUILD_TYPE=$(echo "$BUILD_TYPE" | tr "[:upper:]" "[:lower:]")
|
||||
case "$BUILD_TYPE" in
|
||||
@@ -49,7 +50,8 @@ case "$CMD" in
|
||||
"test")
|
||||
if [ -z "$FALCO_VERSION" ]; then
|
||||
echo "Automatically figuring out Falco version."
|
||||
FALCO_VERSION=$("$BUILD_DIR/$BUILD_TYPE/userspace/falco/falco" --version | head -n 1 | cut -d' ' -f3 | tr -d '\r')
|
||||
FALCO_VERSION_FULL=$("$BUILD_DIR/$BUILD_TYPE/userspace/falco/falco" --version)
|
||||
FALCO_VERSION=$(echo "$FALCO_VERSION_FULL" | head -n 1 | cut -d' ' -f3 | tr -d '\r')
|
||||
echo "Falco version: $FALCO_VERSION"
|
||||
fi
|
||||
if [ -z "$FALCO_VERSION" ]; then
|
||||
|
||||
@@ -28,10 +28,7 @@
|
||||
# The files will be read in the order presented here, so make sure if
|
||||
# you have overrides they appear in later files.
|
||||
rules_file:
|
||||
- /etc/falco/falco_rules.yaml
|
||||
- /etc/falco/falco_rules.local.yaml
|
||||
- /etc/falco/k8s_audit_rules.yaml
|
||||
- /etc/falco/rules.d
|
||||
- /tmp/falco
|
||||
|
||||
# If true, the times displayed in log messages and output messages
|
||||
# will be in ISO 8601. By default, times are displayed in the local
|
||||
|
||||
133
proposals/20201025-drivers-storage-s3.md
Normal file
133
proposals/20201025-drivers-storage-s3.md
Normal file
@@ -0,0 +1,133 @@
|
||||
# Falco Drivers Storage S3
|
||||
|
||||
## Introduction
|
||||
|
||||
In the past days, as many people probably noticed, Bintray started rate-limiting our users, effectively preventing them from downloading any kernel module, rpm/deb package or any pre-built dependency we host there.
|
||||
|
||||
This does not only interrupt the workflow of our users but also the workflow of the contributors, since without bintray most of our container images and CMake files can’t download the dependencies we mirror.
|
||||
|
||||
### What is the cause?
|
||||
|
||||
We had a spike in adoption apparently, either a user with many nodes or an increased number of users. We don’t know this detail specifically yet because bintray does not give us very fine-grained statistics on this.
|
||||
|
||||
This is the 30-days history:
|
||||
|
||||

|
||||
|
||||
As you can see, we can only see that they downloaded the latest kernel module driver version, however we can’t see if:
|
||||
|
||||
* It’s a single source or many different users
|
||||
|
||||
* What is the kernel/OS they are using
|
||||
|
||||
### What do we host on Bintray?
|
||||
|
||||
* RPM packages: high traffic but very manageable ~90k downloads a month
|
||||
|
||||
* Deb packages:low traffic ~5k downloads a month
|
||||
|
||||
* Pre-built image Dependencies: low traffic, will eventually disappear in the future
|
||||
|
||||
* Kernel modules: very high traffic, 700k downloads in 10 days, this is what is causing the current problems. They are primarily used by users of our container images.
|
||||
|
||||
* eBPF probes: low traffic ~5k downloads a month
|
||||
|
||||
### Motivations to go to S3 instead of Bintray for the Drivers
|
||||
|
||||
Bintray does an excellent service at building the rpm/deb structures for us, however we also use them for S3-like storage for the drivers. We have ten thousand files hosted there and the combinations are infinite.
|
||||
|
||||
|
||||
Before today, we had many issues with storage even without the spike in users we are seeing since the last ten days.
|
||||
|
||||
## Context on AWS
|
||||
|
||||
Amazon AWS, recently gave credits to the Falco project to operate some parts of the infrastructure on AWS. The CNCF is providing a sub-account we are already using for the migration of the other pieces (like Prow).
|
||||
|
||||
## Interactions with other teams and the CNCF
|
||||
|
||||
* The setup on the AWS account side already done, this is all technical work.
|
||||
|
||||
* We need to open a CNCF service account ticket for the download.falco.org subdomain to point to the S3 bucket we want to use
|
||||
|
||||
## The Plan
|
||||
|
||||
We want to propose to move the drivers and the container dependencies to S3.
|
||||
|
||||
#### Moving means:
|
||||
|
||||
* We create a public S3 bucket with[ stats enabled](https://docs.aws.amazon.com/AmazonS3/latest/dev/analytics-storage-class.html)
|
||||
|
||||
* We attach the bucket to a cloudfront distribution behind the download.falco.org subdomain
|
||||
|
||||
* We move the current content keeping the same web server directory structure
|
||||
|
||||
* We change the Falco Dockerfiles and driver loader script accordingly
|
||||
|
||||
* We update test-infra to push the drivers to S3
|
||||
|
||||
* Once we have the drivers in S3, we can ask bintray to relax the limits for this month so that our users are able to download the other packages we keep there. Otherwise they will have to wait until November 1st. We only want to do that after the moving because otherwise we will hit the limits pretty quickly.
|
||||
|
||||
#### The repositories we want to move are:
|
||||
|
||||
* [https://bintray.com/falcosecurity/driver](https://bintray.com/falcosecurity/driver) will become https://download.falco.org/driver
|
||||
|
||||
* [https://bintray.com/falcosecurity/dependencies](https://bintray.com/falcosecurity/dependencies) will become https://download.falco.org/dependencies
|
||||
|
||||
#### Changes in Falco
|
||||
|
||||
* [Search for bintray ](https://github.com/falcosecurity/falco/search?p=2&q=bintray)on the Falco repo and replace the URL for the CMake and Docker files.
|
||||
|
||||
* It’s very important to change the DRIVERS_REPO environment variable [here](https://github.com/falcosecurity/falco/blob/0a33f555eb8e019806b46fea8b80a6302a935421/CMakeLists.txt#L86) - this is what updates the falco-driver-loader scripts that the users and container images use to fetch the module
|
||||
|
||||
#### Changes in Test Infra
|
||||
|
||||
* We need to use the S3 cli instead of jfrog cli to upload to the s3 bucket after building [here](https://github.com/falcosecurity/test-infra/blob/master/.circleci/config.yml)
|
||||
|
||||
* We can probably remove jfrog from that repo since it only deals with drivers and drivers are being put on S3 now
|
||||
|
||||
* Instructions on how to setup the S3 directory structure [here](https://falco.org/docs/installation/#install-driver)
|
||||
|
||||
* `/$driver_version$/falco_$target$_$kernelrelease$_$kernelversion$.[ko|o]`
|
||||
|
||||
#### Changes to Falco website
|
||||
|
||||
* Changes should not be necessary, we are not updating the way people install Falco but only the driver. The driver is managed by a script we can change.
|
||||
|
||||
## Mitigation and next steps for the users
|
||||
|
||||
* **The average users should be good to go now, Bintray raised our limits and we have some room to do this without requiring manual steps on your end**
|
||||
|
||||
* **Users that can’t wait for us to have the S3 setup done: **can setup an S3 as driver repo themselves, push the drivers they need to it after compiling them (they can use [Driverkit](https://github.com/falcosecurity/driverkit) for that) Instructions on how to setup the S3 directory structure [here](https://falco.org/docs/installation/#install-driver).
|
||||
|
||||
* **Users that can’t wait but don’t want to setup a webserver themselves**: the falco-driver-loader script can also compile the module for you. Make sure to install the kernel-headers on your nodes.
|
||||
|
||||
* **Users that can wait** we will approve this document and act on the plan described here by providing the DRIVERS_REPO at [https://download.falco.org/driver](https://download.falco.org/driver) that then you can use
|
||||
|
||||
### How to use an alternative DRIVERS_REPO ?
|
||||
|
||||
**On bash:**
|
||||
|
||||
export DRIVERS_REPO=https://your-url-here
|
||||
|
||||
**Docker**
|
||||
|
||||
Pass it as environment variable using the docker run flag -e - for example:
|
||||
|
||||
docker run -e DRIVERS_REPO=[https://your-url-here](https://your-url-here)
|
||||
|
||||
**Kubernetes**
|
||||
|
||||
spec:
|
||||
|
||||
containers:
|
||||
|
||||
- env:
|
||||
|
||||
- name: DRIVERS_REPO
|
||||
|
||||
value: https://your-url-here
|
||||
|
||||
## Release
|
||||
|
||||
Next release is on December 1st, we want to rollout a hotfix 0.26.2 release that only contains the updated script before that date so that users don’t get confused and we can just tell them "update Falco" to get the thing working again.
|
||||
|
||||
BIN
proposals/20201025-drivers-storage-s3_downloads.png
Normal file
BIN
proposals/20201025-drivers-storage-s3_downloads.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 43 KiB |
@@ -522,7 +522,7 @@
|
||||
- macro: container_started
|
||||
condition: >
|
||||
((evt.type = container or
|
||||
(evt.type=execve and evt.dir=< and proc.vpid=1)) and
|
||||
(spawned_process and proc.vpid=1)) and
|
||||
container.image.repository != incomplete)
|
||||
|
||||
- macro: interactive
|
||||
@@ -1918,6 +1918,7 @@
|
||||
- list: falco_sensitive_mount_images
|
||||
items: [
|
||||
docker.io/sysdig/falco, docker.io/sysdig/sysdig, sysdig/falco, sysdig/sysdig,
|
||||
docker.io/falcosecurity/falco, falcosecurity/falco,
|
||||
gcr.io/google_containers/hyperkube,
|
||||
gcr.io/google_containers/kube-proxy, docker.io/calico/node,
|
||||
docker.io/rook/toolbox, docker.io/cloudnativelabs/kube-router, docker.io/consul,
|
||||
@@ -2424,9 +2425,9 @@
|
||||
- rule: Contact K8S API Server From Container
|
||||
desc: Detect attempts to contact the K8S API Server from a container
|
||||
condition: >
|
||||
evt.type=connect and evt.dir=< and
|
||||
evt.type=connect and evt.dir=< and
|
||||
(fd.typechar=4 or fd.typechar=6) and
|
||||
container and
|
||||
container and
|
||||
not k8s_containers and
|
||||
k8s_api_server and
|
||||
not user_known_contact_k8s_api_server_activities
|
||||
@@ -2646,7 +2647,7 @@
|
||||
- rule: Delete or rename shell history
|
||||
desc: Detect shell history deletion
|
||||
condition: >
|
||||
(modify_shell_history or truncate_shell_history) and
|
||||
(modify_shell_history or truncate_shell_history) and
|
||||
not var_lib_docker_filepath and
|
||||
not proc.name in (docker_binaries)
|
||||
output: >
|
||||
@@ -2728,10 +2729,18 @@
|
||||
- macro: remote_file_copy_procs
|
||||
condition: (proc.name in (remote_file_copy_binaries))
|
||||
|
||||
# Users should overwrite this macro to specify conditions under which a
|
||||
# Custom condition for use of remote file copy tool in container
|
||||
- macro: user_known_remote_file_copy_activities
|
||||
condition: (never_true)
|
||||
|
||||
- rule: Launch Remote File Copy Tools in Container
|
||||
desc: Detect remote file copy tools launched in container
|
||||
condition: >
|
||||
spawned_process and container and remote_file_copy_procs
|
||||
spawned_process
|
||||
and container
|
||||
and remote_file_copy_procs
|
||||
and not user_known_remote_file_copy_activities
|
||||
output: >
|
||||
Remote file copy tool launched in container (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline parent_process=%proc.pname
|
||||
container_id=%container.id container_name=%container.name image=%container.image.repository:%container.image.tag)
|
||||
@@ -2872,7 +2881,7 @@
|
||||
tags: [container, mitre_execution]
|
||||
|
||||
|
||||
# This rule is enabled by default.
|
||||
# This rule is enabled by default.
|
||||
# If you want to disable it, modify the following macro.
|
||||
- macro: consider_packet_socket_communication
|
||||
condition: (always_true)
|
||||
@@ -3022,6 +3031,15 @@
|
||||
output: Drift detected (open+create), new executable created in a container (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline filename=%evt.arg.filename name=%evt.arg.name mode=%evt.arg.mode event=%evt.type)
|
||||
priority: ERROR
|
||||
|
||||
- list: c2_server_ip_list
|
||||
items: []
|
||||
|
||||
- rule: Outbound Connection to C2 Servers
|
||||
desc: Detect outbound connection to command & control servers
|
||||
condition: outbound and fd.sip in (c2_server_ip_list)
|
||||
output: Outbound connection to C2 server (command=%proc.cmdline connection=%fd.name user=%user.name user_loginuid=%user.loginuid container_id=%container.id image=%container.image.repository)
|
||||
priority: WARNING
|
||||
tags: [network]
|
||||
|
||||
# Application rules have moved to application_rules.yaml. Please look
|
||||
# there if you want to enable them by adding to
|
||||
|
||||
@@ -49,7 +49,8 @@
|
||||
"kubernetes-admin",
|
||||
vertical_pod_autoscaler_users,
|
||||
cluster-autoscaler,
|
||||
"system:addon-manager"
|
||||
"system:addon-manager",
|
||||
"cloud-controller-manager"
|
||||
]
|
||||
|
||||
- rule: Disallowed K8s User
|
||||
|
||||
@@ -41,4 +41,4 @@ stdout_output:
|
||||
|
||||
program_output:
|
||||
enabled: true
|
||||
program: cat > /tmp/falco_outputs/program_output.txt
|
||||
program: cat >> /tmp/falco_outputs/program_output.txt
|
||||
|
||||
42
test/confs/stdout_output.yaml
Normal file
42
test/confs/stdout_output.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.
|
||||
#
|
||||
|
||||
# File containing Falco rules, loaded at startup.
|
||||
rules_file: /etc/falco_rules.yaml
|
||||
|
||||
# Whether to output events in json or text
|
||||
json_output: false
|
||||
|
||||
# Send information logs to stderr and/or syslog Note these are *not* security
|
||||
# notification logs! These are just Falco lifecycle (and possibly error) logs.
|
||||
log_stderr: false
|
||||
log_syslog: false
|
||||
|
||||
# Where security notifications should go.
|
||||
# Multiple outputs can be enabled.
|
||||
|
||||
syslog_output:
|
||||
enabled: false
|
||||
|
||||
file_output:
|
||||
enabled: false
|
||||
|
||||
stdout_output:
|
||||
enabled: true
|
||||
|
||||
program_output:
|
||||
enabled: false
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright (C) 2019 The Falco Authors.
|
||||
#
|
||||
@@ -31,6 +31,7 @@ from avocado.utils import process
|
||||
from watchdog.observers import Observer
|
||||
from watchdog.events import PatternMatchingEventHandler
|
||||
|
||||
|
||||
class FalcoTest(Test):
|
||||
|
||||
def setUp(self):
|
||||
@@ -49,17 +50,20 @@ class FalcoTest(Test):
|
||||
self.stdout_is = self.params.get('stdout_is', '*', default='')
|
||||
self.stderr_is = self.params.get('stderr_is', '*', default='')
|
||||
|
||||
self.stdout_contains = self.params.get('stdout_contains', '*', default='')
|
||||
self.stdout_contains = self.params.get(
|
||||
'stdout_contains', '*', default='')
|
||||
|
||||
if not isinstance(self.stdout_contains, list):
|
||||
self.stdout_contains = [self.stdout_contains]
|
||||
|
||||
self.stderr_contains = self.params.get('stderr_contains', '*', default='')
|
||||
self.stderr_contains = self.params.get(
|
||||
'stderr_contains', '*', default='')
|
||||
|
||||
if not isinstance(self.stderr_contains, list):
|
||||
self.stderr_contains = [self.stderr_contains]
|
||||
|
||||
self.stdout_not_contains = self.params.get('stdout_not_contains', '*', default='')
|
||||
self.stdout_not_contains = self.params.get(
|
||||
'stdout_not_contains', '*', default='')
|
||||
|
||||
if not isinstance(self.stdout_not_contains, list):
|
||||
if self.stdout_not_contains == '':
|
||||
@@ -67,7 +71,8 @@ class FalcoTest(Test):
|
||||
else:
|
||||
self.stdout_not_contains = [self.stdout_not_contains]
|
||||
|
||||
self.stderr_not_contains = self.params.get('stderr_not_contains', '*', default='')
|
||||
self.stderr_not_contains = self.params.get(
|
||||
'stderr_not_contains', '*', default='')
|
||||
|
||||
if not isinstance(self.stderr_not_contains, list):
|
||||
if self.stderr_not_contains == '':
|
||||
@@ -83,15 +88,18 @@ class FalcoTest(Test):
|
||||
self.trace_file = os.path.join(build_dir, "test", self.trace_file)
|
||||
|
||||
self.json_output = self.params.get('json_output', '*', default=False)
|
||||
self.json_include_output_property = self.params.get('json_include_output_property', '*', default=True)
|
||||
self.json_include_output_property = self.params.get(
|
||||
'json_include_output_property', '*', default=True)
|
||||
self.all_events = self.params.get('all_events', '*', default=False)
|
||||
self.priority = self.params.get('priority', '*', default='debug')
|
||||
self.rules_file = self.params.get('rules_file', '*', default=os.path.join(self.basedir, '../rules/falco_rules.yaml'))
|
||||
self.rules_file = self.params.get(
|
||||
'rules_file', '*', default=os.path.join(self.basedir, '../rules/falco_rules.yaml'))
|
||||
|
||||
if not isinstance(self.rules_file, list):
|
||||
self.rules_file = [self.rules_file]
|
||||
|
||||
self.validate_rules_file = self.params.get('validate_rules_file', '*', default=False)
|
||||
self.validate_rules_file = self.params.get(
|
||||
'validate_rules_file', '*', default=False)
|
||||
|
||||
if self.validate_rules_file == False:
|
||||
self.validate_rules_file = []
|
||||
@@ -118,13 +126,15 @@ class FalcoTest(Test):
|
||||
file = os.path.join(self.basedir, file)
|
||||
self.rules_args = self.rules_args + "-r " + file + " "
|
||||
|
||||
self.conf_file = self.params.get('conf_file', '*', default=os.path.join(self.basedir, '../falco.yaml'))
|
||||
self.conf_file = self.params.get(
|
||||
'conf_file', '*', default=os.path.join(self.basedir, '../falco.yaml'))
|
||||
if not os.path.isabs(self.conf_file):
|
||||
self.conf_file = os.path.join(self.basedir, self.conf_file)
|
||||
|
||||
self.run_duration = self.params.get('run_duration', '*', default='')
|
||||
|
||||
self.disabled_rules = self.params.get('disabled_rules', '*', default='')
|
||||
self.disabled_rules = self.params.get(
|
||||
'disabled_rules', '*', default='')
|
||||
|
||||
if self.disabled_rules == '':
|
||||
self.disabled_rules = []
|
||||
@@ -137,7 +147,8 @@ class FalcoTest(Test):
|
||||
for rule in self.disabled_rules:
|
||||
self.disabled_args = self.disabled_args + "-D " + rule + " "
|
||||
|
||||
self.detect_counts = self.params.get('detect_counts', '*', default=False)
|
||||
self.detect_counts = self.params.get(
|
||||
'detect_counts', '*', default=False)
|
||||
if self.detect_counts == False:
|
||||
self.detect_counts = {}
|
||||
else:
|
||||
@@ -147,7 +158,8 @@ class FalcoTest(Test):
|
||||
detect_counts[key] = value
|
||||
self.detect_counts = detect_counts
|
||||
|
||||
self.rules_warning = self.params.get('rules_warning', '*', default=False)
|
||||
self.rules_warning = self.params.get(
|
||||
'rules_warning', '*', default=False)
|
||||
if self.rules_warning == False:
|
||||
self.rules_warning = set()
|
||||
else:
|
||||
@@ -172,9 +184,11 @@ class FalcoTest(Test):
|
||||
|
||||
self.package = self.params.get('package', '*', default='None')
|
||||
|
||||
self.addl_docker_run_args = self.params.get('addl_docker_run_args', '*', default='')
|
||||
self.addl_docker_run_args = self.params.get(
|
||||
'addl_docker_run_args', '*', default='')
|
||||
|
||||
self.copy_local_driver = self.params.get('copy_local_driver', '*', default=False)
|
||||
self.copy_local_driver = self.params.get(
|
||||
'copy_local_driver', '*', default=False)
|
||||
|
||||
# Used by possibly_copy_local_driver as well as docker run
|
||||
self.module_dir = os.path.expanduser("~/.falco")
|
||||
@@ -197,9 +211,33 @@ class FalcoTest(Test):
|
||||
os.makedirs(filedir)
|
||||
self.outputs = outputs
|
||||
|
||||
self.output_strictly_contains = self.params.get(
|
||||
'output_strictly_contains', '*', default='')
|
||||
|
||||
if self.output_strictly_contains == '':
|
||||
self.output_strictly_contains = {}
|
||||
else:
|
||||
output_strictly_contains = []
|
||||
for item in self.output_strictly_contains:
|
||||
for key, value in list(item.items()):
|
||||
output = {}
|
||||
output['actual'] = key
|
||||
output['expected'] = value
|
||||
output_strictly_contains.append(output)
|
||||
if not output['actual'] == 'stdout':
|
||||
# Clean up file from previous tests, if any
|
||||
if os.path.exists(output['actual']):
|
||||
os.remove(output['actual'])
|
||||
# Create the parent directory for the file if it doesn't exist.
|
||||
filedir = os.path.dirname(output['actual'])
|
||||
if not os.path.isdir(filedir):
|
||||
os.makedirs(filedir)
|
||||
self.output_strictly_contains = output_strictly_contains
|
||||
|
||||
self.grpcurl_res = None
|
||||
self.grpc_observer = None
|
||||
self.grpc_address = self.params.get('address', 'grpc/*', default='/var/run/falco.sock')
|
||||
self.grpc_address = self.params.get(
|
||||
'address', 'grpc/*', default='/var/run/falco.sock')
|
||||
if self.grpc_address.startswith("unix://"):
|
||||
self.is_grpc_using_unix_socket = True
|
||||
self.grpc_address = self.grpc_address[len("unix://"):]
|
||||
@@ -211,21 +249,22 @@ class FalcoTest(Test):
|
||||
self.grpc_results = self.params.get('results', 'grpc/*', default='')
|
||||
if self.grpc_results == '':
|
||||
self.grpc_results = []
|
||||
else:
|
||||
else:
|
||||
if type(self.grpc_results) == str:
|
||||
self.grpc_results = [self.grpc_results]
|
||||
|
||||
self.disable_tags = self.params.get('disable_tags', '*', default='')
|
||||
|
||||
if self.disable_tags == '':
|
||||
self.disable_tags=[]
|
||||
self.disable_tags = []
|
||||
|
||||
self.run_tags = self.params.get('run_tags', '*', default='')
|
||||
|
||||
if self.run_tags == '':
|
||||
self.run_tags=[]
|
||||
self.run_tags = []
|
||||
|
||||
self.time_iso_8601 = self.params.get('time_iso_8601', '*', default=False)
|
||||
self.time_iso_8601 = self.params.get(
|
||||
'time_iso_8601', '*', default=False)
|
||||
|
||||
def tearDown(self):
|
||||
if self.package != 'None':
|
||||
@@ -244,7 +283,8 @@ class FalcoTest(Test):
|
||||
self.log.debug("Actual warning rules: {}".format(found_warning))
|
||||
|
||||
if found_warning != self.rules_warning:
|
||||
self.fail("Expected rules with warnings {} does not match actual rules with warnings {}".format(self.rules_warning, found_warning))
|
||||
self.fail("Expected rules with warnings {} does not match actual rules with warnings {}".format(
|
||||
self.rules_warning, found_warning))
|
||||
|
||||
def check_rules_events(self, res):
|
||||
|
||||
@@ -255,50 +295,60 @@ class FalcoTest(Test):
|
||||
events = set(match.group(2).split(","))
|
||||
found_events[rule] = events
|
||||
|
||||
self.log.debug("Expected events for rules: {}".format(self.rules_events))
|
||||
self.log.debug(
|
||||
"Expected events for rules: {}".format(self.rules_events))
|
||||
self.log.debug("Actual events for rules: {}".format(found_events))
|
||||
|
||||
for rule in list(found_events.keys()):
|
||||
if found_events.get(rule) != self.rules_events.get(rule):
|
||||
self.fail("rule {}: expected events {} differs from actual events {}".format(rule, self.rules_events.get(rule), found_events.get(rule)))
|
||||
self.fail("rule {}: expected events {} differs from actual events {}".format(
|
||||
rule, self.rules_events.get(rule), found_events.get(rule)))
|
||||
|
||||
def check_detections(self, res):
|
||||
# Get the number of events detected.
|
||||
match = re.search('Events detected: (\d+)', res.stdout.decode("utf-8"))
|
||||
if match is None:
|
||||
self.fail("Could not find a line 'Events detected: <count>' in falco output")
|
||||
self.fail(
|
||||
"Could not find a line 'Events detected: <count>' in falco output")
|
||||
|
||||
events_detected = int(match.group(1))
|
||||
|
||||
if not self.should_detect and events_detected > 0:
|
||||
self.fail("Detected {} events when should have detected none".format(events_detected))
|
||||
self.fail("Detected {} events when should have detected none".format(
|
||||
events_detected))
|
||||
|
||||
if self.should_detect:
|
||||
if events_detected == 0:
|
||||
self.fail("Detected {} events when should have detected > 0".format(events_detected))
|
||||
self.fail("Detected {} events when should have detected > 0".format(
|
||||
events_detected))
|
||||
|
||||
for level in self.detect_level:
|
||||
level_line = '(?i){}: (\d+)'.format(level)
|
||||
match = re.search(level_line, res.stdout.decode("utf-8"))
|
||||
|
||||
if match is None:
|
||||
self.fail("Could not find a line '{}: <count>' in falco output".format(level))
|
||||
self.fail(
|
||||
"Could not find a line '{}: <count>' in falco output".format(level))
|
||||
|
||||
events_detected = int(match.group(1))
|
||||
|
||||
if not events_detected > 0:
|
||||
self.fail("Detected {} events at level {} when should have detected > 0".format(events_detected, level))
|
||||
self.fail("Detected {} events at level {} when should have detected > 0".format(
|
||||
events_detected, level))
|
||||
|
||||
def check_detections_by_rule(self, res):
|
||||
# Get the number of events detected for each rule. Must match the expected counts.
|
||||
match = re.search('Triggered rules by rule name:(.*)', res.stdout.decode("utf-8"), re.DOTALL)
|
||||
match = re.search('Triggered rules by rule name:(.*)',
|
||||
res.stdout.decode("utf-8"), re.DOTALL)
|
||||
if match is None:
|
||||
self.fail("Could not find a block 'Triggered rules by rule name: ...' in falco output")
|
||||
self.fail(
|
||||
"Could not find a block 'Triggered rules by rule name: ...' in falco output")
|
||||
|
||||
triggered_rules = match.group(1)
|
||||
|
||||
for rule, count in list(self.detect_counts.items()):
|
||||
expected = '\s{}: (\d+)'.format(re.sub(r'([$\.*+?()[\]{}|^])', r'\\\1', rule))
|
||||
expected = '\s{}: (\d+)'.format(
|
||||
re.sub(r'([$\.*+?()[\]{}|^])', r'\\\1', rule))
|
||||
match = re.search(expected, triggered_rules)
|
||||
|
||||
if match is None:
|
||||
@@ -307,9 +357,11 @@ class FalcoTest(Test):
|
||||
actual_count = int(match.group(1))
|
||||
|
||||
if actual_count != count:
|
||||
self.fail("Different counts for rule {}: expected={}, actual={}".format(rule, count, actual_count))
|
||||
self.fail("Different counts for rule {}: expected={}, actual={}".format(
|
||||
rule, count, actual_count))
|
||||
else:
|
||||
self.log.debug("Found expected count for rule {}: {}".format(rule, count))
|
||||
self.log.debug(
|
||||
"Found expected count for rule {}: {}".format(rule, count))
|
||||
|
||||
def check_outputs(self):
|
||||
for output in self.outputs:
|
||||
@@ -324,7 +376,8 @@ class FalcoTest(Test):
|
||||
found = True
|
||||
|
||||
if found == False:
|
||||
self.fail("Could not find a line '{}' in file '{}'".format(output['line'], output['file']))
|
||||
self.fail("Could not find a line '{}' in file '{}'".format(
|
||||
output['line'], output['file']))
|
||||
|
||||
return True
|
||||
|
||||
@@ -341,7 +394,27 @@ class FalcoTest(Test):
|
||||
attrs = ['time', 'rule', 'priority']
|
||||
for attr in attrs:
|
||||
if not attr in obj:
|
||||
self.fail("Falco JSON object {} does not contain property \"{}\"".format(line, attr))
|
||||
self.fail(
|
||||
"Falco JSON object {} does not contain property \"{}\"".format(line, attr))
|
||||
|
||||
def check_output_strictly_contains(self, res):
|
||||
for output in self.output_strictly_contains:
|
||||
# Read the expected output (from a file) and actual output (either from a file or the stdout),
|
||||
# then check if the actual one strictly contains the expected one.
|
||||
|
||||
expected = open(output['expected']).read()
|
||||
|
||||
if output['actual'] == 'stdout':
|
||||
actual = res.stdout.decode("utf-8")
|
||||
else:
|
||||
actual = open(output['actual']).read()
|
||||
|
||||
if expected not in actual:
|
||||
self.fail("Output '{}' does not strictly contains the expected content '{}'".format(
|
||||
output['actual'], output['expected']))
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def install_package(self):
|
||||
|
||||
@@ -360,35 +433,39 @@ class FalcoTest(Test):
|
||||
self.module_dir, self.addl_docker_run_args, image)
|
||||
|
||||
elif self.package.endswith(".deb"):
|
||||
self.falco_binary_path = '/usr/bin/falco';
|
||||
self.falco_binary_path = '/usr/bin/falco'
|
||||
|
||||
package_glob = "{}/{}".format(self.falcodir, self.package)
|
||||
|
||||
matches = glob.glob(package_glob)
|
||||
|
||||
if len(matches) != 1:
|
||||
self.fail("Package path {} did not match exactly 1 file. Instead it matched: {}", package_glob, ",".join(matches))
|
||||
self.fail("Package path {} did not match exactly 1 file. Instead it matched: {}",
|
||||
package_glob, ",".join(matches))
|
||||
|
||||
package_path = matches[0]
|
||||
|
||||
cmdline = "dpkg -i {}".format(package_path)
|
||||
self.log.debug("Installing debian package via \"{}\"".format(cmdline))
|
||||
self.log.debug(
|
||||
"Installing debian package via \"{}\"".format(cmdline))
|
||||
res = process.run(cmdline, timeout=120, sudo=True)
|
||||
|
||||
elif self.package.endswith(".rpm"):
|
||||
self.falco_binary_path = '/usr/bin/falco';
|
||||
self.falco_binary_path = '/usr/bin/falco'
|
||||
|
||||
package_glob = "{}/{}".format(self.falcodir, self.package)
|
||||
|
||||
matches = glob.glob(package_glob)
|
||||
|
||||
if len(matches) != 1:
|
||||
self.fail("Package path {} did not match exactly 1 file. Instead it matched: {}", package_glob, ",".join(matches))
|
||||
self.fail("Package path {} did not match exactly 1 file. Instead it matched: {}",
|
||||
package_glob, ",".join(matches))
|
||||
|
||||
package_path = matches[0]
|
||||
|
||||
cmdline = "rpm -i --nodeps --noscripts {}".format(package_path)
|
||||
self.log.debug("Installing centos package via \"{}\"".format(cmdline))
|
||||
self.log.debug(
|
||||
"Installing centos package via \"{}\"".format(cmdline))
|
||||
res = process.run(cmdline, timeout=120, sudo=True)
|
||||
|
||||
def uninstall_package(self):
|
||||
@@ -398,25 +475,29 @@ class FalcoTest(Test):
|
||||
|
||||
elif self.package.endswith(".rpm"):
|
||||
cmdline = "rpm -e --noscripts --nodeps falco"
|
||||
self.log.debug("Uninstalling centos package via \"{}\"".format(cmdline))
|
||||
self.log.debug(
|
||||
"Uninstalling centos package via \"{}\"".format(cmdline))
|
||||
res = process.run(cmdline, timeout=120, sudo=True)
|
||||
|
||||
elif self.package.endswith(".deb"):
|
||||
cmdline = "dpkg --purge falco"
|
||||
self.log.debug("Uninstalling debian package via \"{}\"".format(cmdline))
|
||||
self.log.debug(
|
||||
"Uninstalling debian package via \"{}\"".format(cmdline))
|
||||
res = process.run(cmdline, timeout=120, sudo=True)
|
||||
|
||||
def possibly_copy_driver(self):
|
||||
# Remove the contents of ~/.falco regardless of copy_local_driver.
|
||||
self.log.debug("Checking for module dir {}".format(self.module_dir))
|
||||
if os.path.isdir(self.module_dir):
|
||||
self.log.info("Removing files below directory {}".format(self.module_dir))
|
||||
self.log.info(
|
||||
"Removing files below directory {}".format(self.module_dir))
|
||||
for rmfile in glob.glob(self.module_dir + "/*"):
|
||||
self.log.debug("Removing file {}".format(rmfile))
|
||||
os.remove(rmfile)
|
||||
|
||||
if self.copy_local_driver:
|
||||
verlines = [str.strip() for str in subprocess.check_output([self.falco_binary_path, "--version"]).splitlines()]
|
||||
verlines = [str.strip() for str in subprocess.check_output(
|
||||
[self.falco_binary_path, "--version"]).splitlines()]
|
||||
verstr = verlines[0].decode("utf-8")
|
||||
self.log.info("verstr {}".format(verstr))
|
||||
falco_version = verstr.split(" ")[2]
|
||||
@@ -428,10 +509,12 @@ class FalcoTest(Test):
|
||||
|
||||
# falco-driver-loader has a more comprehensive set of ways to
|
||||
# find the config hash. We only look at /boot/config-<kernel release>
|
||||
md5_output = subprocess.check_output(["md5sum", "/boot/config-{}".format(kernel_release)]).rstrip()
|
||||
md5_output = subprocess.check_output(
|
||||
["md5sum", "/boot/config-{}".format(kernel_release)]).rstrip()
|
||||
config_hash = md5_output.split(" ")[0]
|
||||
|
||||
probe_filename = "falco-{}-{}-{}-{}.ko".format(falco_version, arch, kernel_release, config_hash)
|
||||
probe_filename = "falco-{}-{}-{}-{}.ko".format(
|
||||
falco_version, arch, kernel_release, config_hash)
|
||||
driver_path = os.path.join(self.falcodir, "driver", "falco.ko")
|
||||
module_path = os.path.join(self.module_dir, probe_filename)
|
||||
self.log.debug("Copying {} to {}".format(driver_path, module_path))
|
||||
@@ -442,20 +525,22 @@ class FalcoTest(Test):
|
||||
if len(self.grpc_results) > 0:
|
||||
if not self.is_grpc_using_unix_socket:
|
||||
self.fail("This test suite supports gRPC with unix socket only")
|
||||
|
||||
cmdline = "grpcurl -import-path ../userspace/falco " \
|
||||
"-proto {} -plaintext -unix {} " \
|
||||
"{}/{}".format(self.grpc_proto, self.grpc_address, self.grpc_service, self.grpc_method)
|
||||
|
||||
cmdline = "grpcurl -format text -import-path ../userspace/falco " \
|
||||
"-proto {} -plaintext -unix {} " \
|
||||
"{}/{}".format(self.grpc_proto, self.grpc_address,
|
||||
self.grpc_service, self.grpc_method)
|
||||
that = self
|
||||
|
||||
class GRPCUnixSocketEventHandler(PatternMatchingEventHandler):
|
||||
def on_created(self, event):
|
||||
# that.log.info("EVENT: {}", event)
|
||||
that.grpcurl_res = process.run(cmdline)
|
||||
|
||||
|
||||
path = os.path.dirname(self.grpc_address)
|
||||
process.run("mkdir -p {}".format(path))
|
||||
event_handler = GRPCUnixSocketEventHandler(patterns=['*'],
|
||||
ignore_directories=True)
|
||||
ignore_directories=True)
|
||||
self.grpc_observer = Observer()
|
||||
self.grpc_observer.schedule(event_handler, path, recursive=False)
|
||||
self.grpc_observer.start()
|
||||
@@ -470,19 +555,19 @@ class FalcoTest(Test):
|
||||
for exp_result in self.grpc_results:
|
||||
found = False
|
||||
for line in self.grpcurl_res.stdout.decode("utf-8").splitlines():
|
||||
match = re.search(exp_result, line)
|
||||
|
||||
if match is not None:
|
||||
if exp_result in line:
|
||||
found = True
|
||||
break
|
||||
|
||||
if found == False:
|
||||
self.fail("Could not find a line '{}' in gRPC responses".format(exp_result))
|
||||
|
||||
self.fail(
|
||||
"Could not find a line with '{}' in gRPC responses (protobuf text".format(exp_result))
|
||||
|
||||
def test(self):
|
||||
self.log.info("Trace file %s", self.trace_file)
|
||||
|
||||
self.falco_binary_path = '{}/userspace/falco/falco'.format(self.falcodir)
|
||||
self.falco_binary_path = '{}/userspace/falco/falco'.format(
|
||||
self.falcodir)
|
||||
|
||||
self.possibly_copy_driver()
|
||||
|
||||
@@ -501,9 +586,11 @@ class FalcoTest(Test):
|
||||
if self.psp_file != "":
|
||||
|
||||
if not os.path.isfile(self.psp_conv_path):
|
||||
self.log.info("Downloading {} to {}".format(self.psp_conv_url, self.psp_conv_path))
|
||||
self.log.info("Downloading {} to {}".format(
|
||||
self.psp_conv_url, self.psp_conv_path))
|
||||
|
||||
urllib.request.urlretrieve(self.psp_conv_url, self.psp_conv_path)
|
||||
urllib.request.urlretrieve(
|
||||
self.psp_conv_url, self.psp_conv_path)
|
||||
os.chmod(self.psp_conv_path, stat.S_IEXEC)
|
||||
|
||||
conv_cmd = '{} convert psp --psp-path {} --rules-path {}'.format(
|
||||
@@ -521,7 +608,6 @@ class FalcoTest(Test):
|
||||
psp_rules = myfile.read()
|
||||
self.log.debug("Converted Rules: {}".format(psp_rules))
|
||||
|
||||
|
||||
# Run falco
|
||||
cmd = '{} {} {} -c {} {} -o json_output={} -o json_include_output_property={} -o priority={} -v'.format(
|
||||
self.falco_binary_path, self.rules_args, self.disabled_args, self.conf_file, trace_arg, self.json_output, self.json_include_output_property, self.priority)
|
||||
@@ -557,22 +643,26 @@ class FalcoTest(Test):
|
||||
for pattern in self.stderr_contains:
|
||||
match = re.search(pattern, res.stderr.decode("utf-8"))
|
||||
if match is None:
|
||||
self.fail("Stderr of falco process did not contain content matching {}".format(pattern))
|
||||
self.fail(
|
||||
"Stderr of falco process did not contain content matching {}".format(pattern))
|
||||
|
||||
for pattern in self.stdout_contains:
|
||||
match = re.search(pattern, res.stdout.decode("utf-8"))
|
||||
if match is None:
|
||||
self.fail("Stdout of falco process '{}' did not contain content matching {}".format(res.stdout.decode("utf-8"), pattern))
|
||||
self.fail("Stdout of falco process '{}' did not contain content matching {}".format(
|
||||
res.stdout.decode("utf-8"), pattern))
|
||||
|
||||
for pattern in self.stderr_not_contains:
|
||||
match = re.search(pattern, res.stderr.decode("utf-8"))
|
||||
if match is not None:
|
||||
self.fail("Stderr of falco process contained content matching {} when it should have not".format(pattern))
|
||||
self.fail(
|
||||
"Stderr of falco process contained content matching {} when it should have not".format(pattern))
|
||||
|
||||
for pattern in self.stdout_not_contains:
|
||||
match = re.search(pattern, res.stdout.decode("utf-8"))
|
||||
if match is not None:
|
||||
self.fail("Stdout of falco process '{}' did contain content matching {} when it should have not".format(res.stdout.decode("utf-8"), pattern))
|
||||
self.fail("Stdout of falco process '{}' did contain content matching {} when it should have not".format(
|
||||
res.stdout.decode("utf-8"), pattern))
|
||||
|
||||
if res.exit_status != self.exit_status:
|
||||
self.error("Falco command \"{}\" exited with unexpected return value {} (!= {})".format(
|
||||
@@ -590,6 +680,7 @@ class FalcoTest(Test):
|
||||
self.check_detections_by_rule(res)
|
||||
self.check_json_output(res)
|
||||
self.check_outputs()
|
||||
self.check_output_strictly_contains(res)
|
||||
self.check_grpc()
|
||||
pass
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (C) 2016-2018 The Falco Authors..
|
||||
# Copyright (C) 2020 The Falco Authors.
|
||||
#
|
||||
# This file is part of falco.
|
||||
#
|
||||
@@ -652,25 +652,50 @@ trace_files: !mux
|
||||
trace_file: trace_files/cat_write.scap
|
||||
stdout_contains: "Warning An open was seen .cport=<NA> command=cat /dev/null."
|
||||
|
||||
file_output:
|
||||
stdout_output_strict:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- rules/single_rule.yaml
|
||||
conf_file: confs/stdout_output.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
time_iso_8601: true
|
||||
output_strictly_contains:
|
||||
- stdout: output_files/single_rule_with_cat_write.txt
|
||||
|
||||
stdout_output_json_strict:
|
||||
json_output: True
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- rules/single_rule.yaml
|
||||
conf_file: confs/stdout_output.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
time_iso_8601: true
|
||||
output_strictly_contains:
|
||||
- stdout: output_files/single_rule_with_cat_write.json
|
||||
|
||||
file_output_strict:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- rules/single_rule.yaml
|
||||
conf_file: confs/file_output.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
outputs:
|
||||
- /tmp/falco_outputs/file_output.txt: Warning An open was seen
|
||||
time_iso_8601: true
|
||||
output_strictly_contains:
|
||||
- /tmp/falco_outputs/file_output.txt: output_files/single_rule_with_cat_write.txt
|
||||
|
||||
program_output:
|
||||
program_output_strict:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- rules/single_rule.yaml
|
||||
conf_file: confs/program_output.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
outputs:
|
||||
- /tmp/falco_outputs/program_output.txt: Warning An open was seen
|
||||
time_iso_8601: true
|
||||
output_strictly_contains:
|
||||
- /tmp/falco_outputs/program_output.txt: output_files/single_rule_with_cat_write.txt
|
||||
|
||||
grpc_unix_socket_outputs:
|
||||
detect: True
|
||||
@@ -680,13 +705,26 @@ trace_files: !mux
|
||||
conf_file: confs/grpc_unix_socket.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
run_duration: 5
|
||||
time_iso_8601: true
|
||||
grpc:
|
||||
address: unix:///tmp/falco/falco.sock
|
||||
proto: outputs.proto
|
||||
service: falco.outputs.service
|
||||
method: get
|
||||
# protobuf text format
|
||||
results:
|
||||
- "Warning An open was seen"
|
||||
- "seconds:1470327477 nanos:881781397"
|
||||
- "priority: WARNING"
|
||||
- "rule: \"open_from_cat\""
|
||||
- "output: \"2016-08-04T16:17:57.881781397+0000: Warning An open was seen (command=cat /dev/null)\""
|
||||
# output fields
|
||||
- "key: \"evt.time.iso8601\""
|
||||
- "value: \"2016-08-04T16:17:57.881781397+0000\""
|
||||
- "key: \"proc.cmdline\""
|
||||
- "value: \"cat /dev/null\""
|
||||
# For the hostname, since we don't know that beforehand,
|
||||
# only check the field presence
|
||||
- "hostname: "
|
||||
|
||||
detect_counts:
|
||||
detect: True
|
||||
|
||||
8
test/output_files/single_rule_with_cat_write.json
Normal file
8
test/output_files/single_rule_with_cat_write.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{"output":"2016-08-04T16:17:57.881781397+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","time":"2016-08-04T16:17:57.881781397Z", "output_fields": {"evt.time.iso8601":1470327477881781397,"proc.cmdline":"cat /dev/null"}}
|
||||
{"output":"2016-08-04T16:17:57.881785348+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","time":"2016-08-04T16:17:57.881785348Z", "output_fields": {"evt.time.iso8601":1470327477881785348,"proc.cmdline":"cat /dev/null"}}
|
||||
{"output":"2016-08-04T16:17:57.881796705+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","time":"2016-08-04T16:17:57.881796705Z", "output_fields": {"evt.time.iso8601":1470327477881796705,"proc.cmdline":"cat /dev/null"}}
|
||||
{"output":"2016-08-04T16:17:57.881799840+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","time":"2016-08-04T16:17:57.881799840Z", "output_fields": {"evt.time.iso8601":1470327477881799840,"proc.cmdline":"cat /dev/null"}}
|
||||
{"output":"2016-08-04T16:17:57.882003104+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","time":"2016-08-04T16:17:57.882003104Z", "output_fields": {"evt.time.iso8601":1470327477882003104,"proc.cmdline":"cat /dev/null"}}
|
||||
{"output":"2016-08-04T16:17:57.882008208+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","time":"2016-08-04T16:17:57.882008208Z", "output_fields": {"evt.time.iso8601":1470327477882008208,"proc.cmdline":"cat /dev/null"}}
|
||||
{"output":"2016-08-04T16:17:57.882045694+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","time":"2016-08-04T16:17:57.882045694Z", "output_fields": {"evt.time.iso8601":1470327477882045694,"proc.cmdline":"cat /dev/null"}}
|
||||
{"output":"2016-08-04T16:17:57.882054739+0000: Warning An open was seen (command=cat /dev/null)","priority":"Warning","rule":"open_from_cat","time":"2016-08-04T16:17:57.882054739Z", "output_fields": {"evt.time.iso8601":1470327477882054739,"proc.cmdline":"cat /dev/null"}}
|
||||
8
test/output_files/single_rule_with_cat_write.txt
Normal file
8
test/output_files/single_rule_with_cat_write.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
2016-08-04T16:17:57.881781397+0000: Warning An open was seen (command=cat /dev/null)
|
||||
2016-08-04T16:17:57.881785348+0000: Warning An open was seen (command=cat /dev/null)
|
||||
2016-08-04T16:17:57.881796705+0000: Warning An open was seen (command=cat /dev/null)
|
||||
2016-08-04T16:17:57.881799840+0000: Warning An open was seen (command=cat /dev/null)
|
||||
2016-08-04T16:17:57.882003104+0000: Warning An open was seen (command=cat /dev/null)
|
||||
2016-08-04T16:17:57.882008208+0000: Warning An open was seen (command=cat /dev/null)
|
||||
2016-08-04T16:17:57.882045694+0000: Warning An open was seen (command=cat /dev/null)
|
||||
2016-08-04T16:17:57.882054739+0000: Warning An open was seen (command=cat /dev/null)
|
||||
@@ -38,7 +38,8 @@ if(MINIMAL_BUILD)
|
||||
"${SYSDIG_SOURCE_DIR}/userspace/libsinsp/third-party/jsoncpp"
|
||||
"${SYSDIG_SOURCE_DIR}/userspace/libscap"
|
||||
"${SYSDIG_SOURCE_DIR}/userspace/libsinsp"
|
||||
"${PROJECT_BINARY_DIR}/userspace/engine")
|
||||
"${PROJECT_BINARY_DIR}/userspace/engine"
|
||||
"${PROJECT_SOURCE_DIR}/userspace/libhawk")
|
||||
else()
|
||||
target_include_directories(
|
||||
falco_engine
|
||||
@@ -51,11 +52,17 @@ else()
|
||||
"${SYSDIG_SOURCE_DIR}/userspace/libsinsp/third-party/jsoncpp"
|
||||
"${SYSDIG_SOURCE_DIR}/userspace/libscap"
|
||||
"${SYSDIG_SOURCE_DIR}/userspace/libsinsp"
|
||||
"${PROJECT_BINARY_DIR}/userspace/engine")
|
||||
"${PROJECT_BINARY_DIR}/userspace/engine"
|
||||
"${PROJECT_SOURCE_DIR}/userspace/libhawk")
|
||||
endif()
|
||||
|
||||
target_link_libraries(falco_engine "${FALCO_SINSP_LIBRARY}" "${LPEG_LIB}" "${LYAML_LIB}" "${LIBYAML_LIB}")
|
||||
|
||||
if(DEFINED LIBHAWK_LIBRARIES)
|
||||
message(STATUS "Using externally provided libhawk implementations: ${LIBHAWK_LIBRARIES}")
|
||||
target_link_libraries(falco_engine ${LIBHAWK_LIBRARIES})
|
||||
endif()
|
||||
|
||||
configure_file(config_falco_engine.h.in config_falco_engine.h)
|
||||
|
||||
if(DEFINED FALCO_COMPONENT)
|
||||
|
||||
@@ -26,7 +26,8 @@ limitations under the License.
|
||||
|
||||
#include "formats.h"
|
||||
|
||||
extern "C" {
|
||||
extern "C"
|
||||
{
|
||||
#include "lpeg.h"
|
||||
#include "lyaml.h"
|
||||
}
|
||||
@@ -34,7 +35,6 @@ extern "C" {
|
||||
#include "utils.h"
|
||||
#include "banned.h" // This raises a compilation error when certain functions are used
|
||||
|
||||
|
||||
string lua_on_event = "on_event";
|
||||
string lua_print_stats = "print_stats";
|
||||
|
||||
@@ -42,24 +42,24 @@ using namespace std;
|
||||
|
||||
nlohmann::json::json_pointer falco_engine::k8s_audit_time = "/stageTimestamp"_json_pointer;
|
||||
|
||||
falco_engine::falco_engine(bool seed_rng, const std::string& alternate_lua_dir)
|
||||
: m_rules(NULL), m_next_ruleset_id(0),
|
||||
m_min_priority(falco_common::PRIORITY_DEBUG),
|
||||
m_sampling_ratio(1), m_sampling_multiplier(0),
|
||||
m_replace_container_info(false)
|
||||
falco_engine::falco_engine(bool seed_rng, const std::string &alternate_lua_dir):
|
||||
m_rules(NULL), m_next_ruleset_id(0),
|
||||
m_min_priority(falco_common::PRIORITY_DEBUG),
|
||||
m_sampling_ratio(1), m_sampling_multiplier(0),
|
||||
m_replace_container_info(false)
|
||||
{
|
||||
luaopen_lpeg(m_ls);
|
||||
luaopen_yaml(m_ls);
|
||||
|
||||
m_alternate_lua_dir = alternate_lua_dir;
|
||||
falco_common::init(m_lua_main_filename.c_str(), alternate_lua_dir.c_str());
|
||||
falco_rules::init(m_ls);
|
||||
|
||||
m_sinsp_rules.reset(new falco_sinsp_ruleset());
|
||||
m_k8s_audit_rules.reset(new falco_ruleset());
|
||||
clear_filters();
|
||||
|
||||
if(seed_rng)
|
||||
{
|
||||
srandom((unsigned) getpid());
|
||||
srandom((unsigned)getpid());
|
||||
}
|
||||
|
||||
m_default_ruleset_id = find_ruleset_id(m_default_ruleset);
|
||||
@@ -70,15 +70,24 @@ falco_engine::falco_engine(bool seed_rng, const std::string& alternate_lua_dir)
|
||||
|
||||
falco_engine::~falco_engine()
|
||||
{
|
||||
if (m_rules)
|
||||
if(m_rules)
|
||||
{
|
||||
delete m_rules;
|
||||
}
|
||||
}
|
||||
|
||||
falco_engine *falco_engine::clone()
|
||||
{
|
||||
auto engine = new falco_engine(true, m_alternate_lua_dir);
|
||||
engine->set_inspector(m_inspector);
|
||||
engine->set_extra(m_extra, m_replace_container_info);
|
||||
engine->set_min_priority(m_min_priority);
|
||||
return engine;
|
||||
}
|
||||
|
||||
uint32_t falco_engine::engine_version()
|
||||
{
|
||||
return (uint32_t) FALCO_ENGINE_VERSION;
|
||||
return (uint32_t)FALCO_ENGINE_VERSION;
|
||||
}
|
||||
|
||||
#define DESCRIPTION_TEXT_START 16
|
||||
@@ -144,17 +153,28 @@ void falco_engine::list_fields(bool names_only)
|
||||
}
|
||||
}
|
||||
|
||||
void falco_engine::load_rules(const string &rules_content, bool verbose, bool all_events)
|
||||
void falco_engine::load_rules_file(const string &rules_filename, bool verbose, bool all_events)
|
||||
{
|
||||
uint64_t dummy;
|
||||
ifstream is;
|
||||
|
||||
return load_rules(rules_content, verbose, all_events, dummy);
|
||||
is.open(rules_filename);
|
||||
if(!is.is_open())
|
||||
{
|
||||
throw falco_exception("Could not open rules filename " +
|
||||
rules_filename + " " +
|
||||
"for reading");
|
||||
}
|
||||
|
||||
string rules_content((istreambuf_iterator<char>(is)),
|
||||
istreambuf_iterator<char>());
|
||||
|
||||
load_rules(rules_content, verbose, all_events);
|
||||
}
|
||||
|
||||
void falco_engine::load_rules(const string &rules_content, bool verbose, bool all_events, uint64_t &required_engine_version)
|
||||
void falco_engine::load_rules(const string &rules_content, bool verbose, bool all_events)
|
||||
{
|
||||
// The engine must have been given an inspector by now.
|
||||
if(! m_inspector)
|
||||
if(!m_inspector)
|
||||
{
|
||||
throw falco_exception("No inspector provided");
|
||||
}
|
||||
@@ -166,46 +186,52 @@ void falco_engine::load_rules(const string &rules_content, bool verbose, bool al
|
||||
|
||||
if(!m_rules)
|
||||
{
|
||||
m_rules = new falco_rules(m_inspector,
|
||||
this,
|
||||
m_ls);
|
||||
// Note that falco_formats is added to the lua state used by the falco engine only.
|
||||
// Within the engine, only formats.
|
||||
// Formatter is used, so we can unconditionally set json_output to false.
|
||||
bool json_output = false;
|
||||
bool json_include_output_property = false;
|
||||
falco_formats::init(m_inspector, this, m_ls, json_output, json_include_output_property);
|
||||
m_rules = new falco_rules(m_inspector, this, m_ls);
|
||||
}
|
||||
|
||||
// Note that falco_formats is added to both the lua state used
|
||||
// by the falco engine as well as the separate lua state used
|
||||
// by falco outputs. Within the engine, only
|
||||
// formats.formatter is used, so we can unconditionally set
|
||||
// json_output to false.
|
||||
bool json_output = false;
|
||||
bool json_include_output_property = false;
|
||||
falco_formats::init(m_inspector, this, m_ls, json_output, json_include_output_property);
|
||||
|
||||
m_rules->load_rules(rules_content, verbose, all_events, m_extra, m_replace_container_info, m_min_priority, required_engine_version);
|
||||
}
|
||||
|
||||
void falco_engine::load_rules_file(const string &rules_filename, bool verbose, bool all_events)
|
||||
{
|
||||
uint64_t dummy;
|
||||
// m_sinsp_rules.reset(new falco_sinsp_ruleset());
|
||||
// m_k8s_audit_rules.reset(new falco_ruleset());
|
||||
m_rules->load_rules(rules_content, verbose, all_events, m_extra, m_replace_container_info, m_min_priority, dummy);
|
||||
|
||||
return load_rules_file(rules_filename, verbose, all_events, dummy);
|
||||
m_is_ready = true;
|
||||
|
||||
return;
|
||||
|
||||
//
|
||||
// auto local_rules = new falco_rules(m_inspector, this, m_ls);
|
||||
// try
|
||||
// {
|
||||
// uint64_t dummy;
|
||||
// local_rules->load_rules(rules_content, verbose, all_events, m_extra, m_replace_container_info, m_min_priority, dummy);
|
||||
|
||||
// // m_rules = local_rules
|
||||
// // std::atomic<falco_rules *> lore(m_rules);
|
||||
// // std::atomic_exchange(&lore, local_rules);
|
||||
// // SCHEDULE LOCAL_RULES AS NEXT RULESET
|
||||
// }
|
||||
// catch(const falco_exception &e)
|
||||
// {
|
||||
// // todo
|
||||
// printf("IGNORE BECAUSE OF ERROR LOADING RULESET!\n");
|
||||
// }
|
||||
}
|
||||
|
||||
void falco_engine::load_rules_file(const string &rules_filename, bool verbose, bool all_events, uint64_t &required_engine_version)
|
||||
// // todo(fntlnz): not sure we want this in falco_engine
|
||||
// void falco_engine::watch_rules(bool verbose, bool all_events)
|
||||
// {
|
||||
// hawk_watch_rules((hawk_watch_rules_cb)rules_cb, reinterpret_cast<hawk_engine *>(this));
|
||||
// }
|
||||
|
||||
bool falco_engine::is_ready()
|
||||
{
|
||||
ifstream is;
|
||||
|
||||
is.open(rules_filename);
|
||||
if (!is.is_open())
|
||||
{
|
||||
throw falco_exception("Could not open rules filename " +
|
||||
rules_filename + " " +
|
||||
"for reading");
|
||||
}
|
||||
|
||||
string rules_content((istreambuf_iterator<char>(is)),
|
||||
istreambuf_iterator<char>());
|
||||
|
||||
load_rules(rules_content, verbose, all_events, required_engine_version);
|
||||
return m_is_ready;
|
||||
}
|
||||
|
||||
void falco_engine::enable_rule(const string &substring, bool enabled, const string &ruleset)
|
||||
@@ -273,7 +299,7 @@ uint64_t falco_engine::num_rules_for_ruleset(const std::string &ruleset)
|
||||
uint16_t ruleset_id = find_ruleset_id(ruleset);
|
||||
|
||||
return m_sinsp_rules->num_rules_for_ruleset(ruleset_id) +
|
||||
m_k8s_audit_rules->num_rules_for_ruleset(ruleset_id);
|
||||
m_k8s_audit_rules->num_rules_for_ruleset(ruleset_id);
|
||||
}
|
||||
|
||||
void falco_engine::evttypes_for_ruleset(std::vector<bool> &evttypes, const std::string &ruleset)
|
||||
@@ -312,15 +338,15 @@ unique_ptr<falco_engine::rule_result> falco_engine::process_sinsp_event(sinsp_ev
|
||||
|
||||
if(lua_pcall(m_ls, 1, 3, 0) != 0)
|
||||
{
|
||||
const char* lerr = lua_tostring(m_ls, -1);
|
||||
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, -3);
|
||||
res->rule = p;
|
||||
res->source = "syscall";
|
||||
res->priority_num = (falco_common::priority_type) lua_tonumber(m_ls, -2);
|
||||
res->priority_num = (falco_common::priority_type)lua_tonumber(m_ls, -2);
|
||||
res->format = lua_tostring(m_ls, -1);
|
||||
lua_pop(m_ls, 3);
|
||||
}
|
||||
@@ -334,6 +360,7 @@ unique_ptr<falco_engine::rule_result> falco_engine::process_sinsp_event(sinsp_ev
|
||||
|
||||
unique_ptr<falco_engine::rule_result> falco_engine::process_sinsp_event(sinsp_evt *ev)
|
||||
{
|
||||
// todo(leodido, fntlnz) > pass the last ruleset id
|
||||
return process_sinsp_event(ev, m_default_ruleset_id);
|
||||
}
|
||||
|
||||
@@ -345,7 +372,7 @@ unique_ptr<falco_engine::rule_result> falco_engine::process_k8s_audit_event(json
|
||||
}
|
||||
|
||||
// All k8s audit events have the single tag "1".
|
||||
if(!m_k8s_audit_rules->run((gen_event *) ev, 1, ruleset_id))
|
||||
if(!m_k8s_audit_rules->run((gen_event *)ev, 1, ruleset_id))
|
||||
{
|
||||
return unique_ptr<struct rule_result>();
|
||||
}
|
||||
@@ -360,15 +387,15 @@ unique_ptr<falco_engine::rule_result> falco_engine::process_k8s_audit_event(json
|
||||
|
||||
if(lua_pcall(m_ls, 1, 3, 0) != 0)
|
||||
{
|
||||
const char* lerr = lua_tostring(m_ls, -1);
|
||||
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, -3);
|
||||
res->rule = p;
|
||||
res->source = "k8s_audit";
|
||||
res->priority_num = (falco_common::priority_type) lua_tonumber(m_ls, -2);
|
||||
res->priority_num = (falco_common::priority_type)lua_tonumber(m_ls, -2);
|
||||
res->format = lua_tostring(m_ls, -1);
|
||||
lua_pop(m_ls, 3);
|
||||
}
|
||||
@@ -394,7 +421,7 @@ bool falco_engine::parse_k8s_audit_json(nlohmann::json &j, std::list<json_event>
|
||||
{
|
||||
// Note we only handle a single top level array, to
|
||||
// avoid excessive recursion.
|
||||
if(! parse_k8s_audit_json(item, evts, false))
|
||||
if(!parse_k8s_audit_json(item, evts, false))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -472,7 +499,7 @@ void falco_engine::print_stats()
|
||||
{
|
||||
if(lua_pcall(m_ls, 0, 0, 0) != 0)
|
||||
{
|
||||
const char* lerr = lua_tostring(m_ls, -1);
|
||||
const char *lerr = lua_tostring(m_ls, -1);
|
||||
string err = "Error invoking function print_stats: " + string(lerr);
|
||||
throw falco_exception(err);
|
||||
}
|
||||
@@ -481,21 +508,20 @@ void falco_engine::print_stats()
|
||||
{
|
||||
throw falco_exception("No function " + lua_print_stats + " found in lua rule loader module");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void falco_engine::add_sinsp_filter(string &rule,
|
||||
set<uint32_t> &evttypes,
|
||||
set<uint32_t> &syscalls,
|
||||
set<string> &tags,
|
||||
sinsp_filter* filter)
|
||||
sinsp_filter *filter)
|
||||
{
|
||||
m_sinsp_rules->add(rule, evttypes, syscalls, tags, filter);
|
||||
}
|
||||
|
||||
void falco_engine::add_k8s_audit_filter(string &rule,
|
||||
set<string> &tags,
|
||||
json_event_filter* filter)
|
||||
json_event_filter *filter)
|
||||
{
|
||||
// All k8s audit events have a single tag "1".
|
||||
std::set<uint32_t> event_tags = {1};
|
||||
@@ -537,8 +563,8 @@ inline bool falco_engine::should_drop_evt()
|
||||
return false;
|
||||
}
|
||||
|
||||
double coin = (random() * (1.0/RAND_MAX));
|
||||
return (coin >= (1.0/(m_sampling_multiplier * m_sampling_ratio)));
|
||||
double coin = (random() * (1.0 / RAND_MAX));
|
||||
return (coin >= (1.0 / (m_sampling_multiplier * m_sampling_ratio)));
|
||||
}
|
||||
|
||||
sinsp_filter_factory &falco_engine::sinsp_factory()
|
||||
|
||||
@@ -38,6 +38,11 @@ limitations under the License.
|
||||
#include "config_falco_engine.h"
|
||||
#include "falco_common.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include "hawk.h"
|
||||
}
|
||||
|
||||
//
|
||||
// This class acts as the primary interface between a program and the
|
||||
// falco rules engine. Falco outputs (writing to files/syslog/etc) are
|
||||
@@ -47,9 +52,12 @@ limitations under the License.
|
||||
class falco_engine : public falco_common
|
||||
{
|
||||
public:
|
||||
falco_engine(bool seed_rng=true, const std::string& alternate_lua_dir=FALCO_ENGINE_SOURCE_LUA_DIR);
|
||||
falco_engine(bool seed_rng = true, const std::string &alternate_lua_dir = FALCO_ENGINE_SOURCE_LUA_DIR);
|
||||
virtual ~falco_engine();
|
||||
|
||||
falco_engine(const falco_engine &rhs);
|
||||
falco_engine *clone();
|
||||
|
||||
// A given engine has a version which identifies the fields
|
||||
// and rules file format it supports. This version will change
|
||||
// any time the code that handles rules files, expression
|
||||
@@ -57,7 +65,7 @@ public:
|
||||
static uint32_t engine_version();
|
||||
|
||||
// Print to stdout (using printf) a description of each field supported by this engine.
|
||||
void list_fields(bool names_only=false);
|
||||
void list_fields(bool names_only = false);
|
||||
|
||||
//
|
||||
// Load rules either directly or from a filename.
|
||||
@@ -65,12 +73,8 @@ public:
|
||||
void load_rules_file(const std::string &rules_filename, bool verbose, bool all_events);
|
||||
void load_rules(const std::string &rules_content, bool verbose, bool all_events);
|
||||
|
||||
//
|
||||
// Identical to above, but also returns the required engine version for the file/content.
|
||||
// (If no required engine version is specified, returns 0).
|
||||
//
|
||||
void load_rules_file(const std::string &rules_filename, bool verbose, bool all_events, uint64_t &required_engine_version);
|
||||
void load_rules(const std::string &rules_content, bool verbose, bool all_events, uint64_t &required_engine_version);
|
||||
// Watch and live-reload rules using an external ABI interface provided by libhawk
|
||||
void watch_rules(bool verbose, bool all_events);
|
||||
|
||||
//
|
||||
// Enable/Disable any rules matching the provided substring.
|
||||
@@ -85,7 +89,6 @@ public:
|
||||
// Wrapper that assumes the default ruleset
|
||||
void enable_rule(const std::string &substring, bool enabled);
|
||||
|
||||
|
||||
// Like enable_rule, but the rule name must be an exact match.
|
||||
void enable_rule_exact(const std::string &rule_name, bool enabled, const std::string &ruleset);
|
||||
|
||||
@@ -154,7 +157,8 @@ public:
|
||||
|
||||
// **Methods Related to k8s audit log events, which are
|
||||
// **represented as json objects.
|
||||
struct rule_result {
|
||||
struct rule_result
|
||||
{
|
||||
gen_event *evt;
|
||||
std::string rule;
|
||||
std::string source;
|
||||
@@ -170,7 +174,7 @@ public:
|
||||
// Returns true if the json object was recognized as a k8s
|
||||
// audit event(s), false otherwise.
|
||||
//
|
||||
bool parse_k8s_audit_json(nlohmann::json &j, std::list<json_event> &evts, bool top=true);
|
||||
bool parse_k8s_audit_json(nlohmann::json &j, std::list<json_event> &evts, bool top = true);
|
||||
|
||||
//
|
||||
// Given an event, check it against the set of rules in the
|
||||
@@ -195,7 +199,7 @@ public:
|
||||
//
|
||||
void add_k8s_audit_filter(std::string &rule,
|
||||
std::set<std::string> &tags,
|
||||
json_event_filter* filter);
|
||||
json_event_filter *filter);
|
||||
|
||||
// **Methods Related to Sinsp Events e.g system calls
|
||||
//
|
||||
@@ -236,13 +240,14 @@ public:
|
||||
std::set<uint32_t> &evttypes,
|
||||
std::set<uint32_t> &syscalls,
|
||||
std::set<std::string> &tags,
|
||||
sinsp_filter* filter);
|
||||
sinsp_filter *filter);
|
||||
|
||||
sinsp_filter_factory &sinsp_factory();
|
||||
json_event_filter_factory &json_factory();
|
||||
|
||||
private:
|
||||
bool is_ready();
|
||||
|
||||
private:
|
||||
static nlohmann::json::json_pointer k8s_audit_time;
|
||||
|
||||
//
|
||||
@@ -262,6 +267,8 @@ private:
|
||||
std::unique_ptr<falco_sinsp_ruleset> m_sinsp_rules;
|
||||
std::unique_ptr<falco_ruleset> m_k8s_audit_rules;
|
||||
|
||||
std::string m_alternate_lua_dir;
|
||||
|
||||
//
|
||||
// Here's how the sampling ratio and multiplier influence
|
||||
// whether or not an event is dropped in
|
||||
@@ -291,5 +298,6 @@ private:
|
||||
|
||||
std::string m_extra;
|
||||
bool m_replace_container_info;
|
||||
};
|
||||
|
||||
bool m_is_ready = false;
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (C) 2019 The Falco Authors.
|
||||
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.
|
||||
@@ -20,24 +20,19 @@ limitations under the License.
|
||||
#include "falco_engine.h"
|
||||
#include "banned.h" // This raises a compilation error when certain functions are used
|
||||
|
||||
|
||||
sinsp* falco_formats::s_inspector = NULL;
|
||||
sinsp *falco_formats::s_inspector = NULL;
|
||||
falco_engine *falco_formats::s_engine = NULL;
|
||||
bool falco_formats::s_json_output = false;
|
||||
bool falco_formats::s_json_include_output_property = true;
|
||||
sinsp_evt_formatter_cache *falco_formats::s_formatters = NULL;
|
||||
std::unique_ptr<sinsp_evt_formatter_cache> falco_formats::s_formatters = NULL;
|
||||
|
||||
const static struct luaL_reg ll_falco [] =
|
||||
{
|
||||
{"formatter", &falco_formats::formatter},
|
||||
{"free_formatter", &falco_formats::free_formatter},
|
||||
{"free_formatters", &falco_formats::free_formatters},
|
||||
{"format_event", &falco_formats::format_event},
|
||||
{"resolve_tokens", &falco_formats::resolve_tokens},
|
||||
{NULL,NULL}
|
||||
};
|
||||
const static struct luaL_reg ll_falco[] =
|
||||
{
|
||||
{"formatter", &falco_formats::lua_formatter},
|
||||
{"free_formatter", &falco_formats::lua_free_formatter},
|
||||
{NULL, NULL}};
|
||||
|
||||
void falco_formats::init(sinsp* inspector,
|
||||
void falco_formats::init(sinsp *inspector,
|
||||
falco_engine *engine,
|
||||
lua_State *ls,
|
||||
bool json_output,
|
||||
@@ -47,15 +42,14 @@ void falco_formats::init(sinsp* inspector,
|
||||
s_engine = engine;
|
||||
s_json_output = json_output;
|
||||
s_json_include_output_property = json_include_output_property;
|
||||
if(!s_formatters)
|
||||
{
|
||||
s_formatters = new sinsp_evt_formatter_cache(s_inspector);
|
||||
}
|
||||
|
||||
// todo(leogr): we should have used std::make_unique, but we cannot since it's not C++14
|
||||
s_formatters = std::unique_ptr<sinsp_evt_formatter_cache>(new sinsp_evt_formatter_cache(s_inspector));
|
||||
|
||||
luaL_openlib(ls, "formats", ll_falco, 0);
|
||||
}
|
||||
|
||||
int falco_formats::formatter(lua_State *ls)
|
||||
int falco_formats::lua_formatter(lua_State *ls)
|
||||
{
|
||||
string source = luaL_checkstring(ls, -2);
|
||||
string format = luaL_checkstring(ls, -1);
|
||||
@@ -64,7 +58,7 @@ int falco_formats::formatter(lua_State *ls)
|
||||
{
|
||||
if(source == "syscall")
|
||||
{
|
||||
sinsp_evt_formatter* formatter;
|
||||
sinsp_evt_formatter *formatter;
|
||||
formatter = new sinsp_evt_formatter(s_inspector, format);
|
||||
lua_pushlightuserdata(ls, formatter);
|
||||
}
|
||||
@@ -75,11 +69,11 @@ int falco_formats::formatter(lua_State *ls)
|
||||
lua_pushlightuserdata(ls, formatter);
|
||||
}
|
||||
}
|
||||
catch(sinsp_exception& e)
|
||||
catch(sinsp_exception &e)
|
||||
{
|
||||
luaL_error(ls, "Invalid output format '%s': '%s'", format.c_str(), e.what());
|
||||
}
|
||||
catch(falco_exception& e)
|
||||
catch(falco_exception &e)
|
||||
{
|
||||
luaL_error(ls, "Invalid output format '%s': '%s'", format.c_str(), e.what());
|
||||
}
|
||||
@@ -87,10 +81,10 @@ int falco_formats::formatter(lua_State *ls)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int falco_formats::free_formatter(lua_State *ls)
|
||||
int falco_formats::lua_free_formatter(lua_State *ls)
|
||||
{
|
||||
if (!lua_islightuserdata(ls, -1) ||
|
||||
!lua_isstring(ls, -2))
|
||||
if(!lua_islightuserdata(ls, -1) ||
|
||||
!lua_isstring(ls, -2))
|
||||
|
||||
{
|
||||
luaL_error(ls, "Invalid argument passed to free_formatter");
|
||||
@@ -100,115 +94,75 @@ int falco_formats::free_formatter(lua_State *ls)
|
||||
|
||||
if(source == "syscall")
|
||||
{
|
||||
sinsp_evt_formatter *formatter = (sinsp_evt_formatter *) lua_topointer(ls, -1);
|
||||
sinsp_evt_formatter *formatter = (sinsp_evt_formatter *)lua_topointer(ls, -1);
|
||||
delete(formatter);
|
||||
}
|
||||
else
|
||||
{
|
||||
json_event_formatter *formatter = (json_event_formatter *) lua_topointer(ls, -1);
|
||||
json_event_formatter *formatter = (json_event_formatter *)lua_topointer(ls, -1);
|
||||
delete(formatter);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int falco_formats::free_formatters(lua_State *ls)
|
||||
string falco_formats::format_event(const gen_event *evt, const std::string &rule, const std::string &source,
|
||||
const std::string &level, const std::string &format)
|
||||
{
|
||||
if(s_formatters)
|
||||
{
|
||||
delete(s_formatters);
|
||||
s_formatters = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int falco_formats::format_event (lua_State *ls)
|
||||
{
|
||||
string line;
|
||||
string json_line;
|
||||
|
||||
if (!lua_isstring(ls, -1) ||
|
||||
!lua_isstring(ls, -2) ||
|
||||
!lua_isstring(ls, -3) ||
|
||||
!lua_isstring(ls, -4) ||
|
||||
!lua_islightuserdata(ls, -5)) {
|
||||
lua_pushstring(ls, "Invalid arguments passed to format_event()");
|
||||
lua_error(ls);
|
||||
}
|
||||
gen_event* evt = (gen_event*)lua_topointer(ls, 1);
|
||||
const char *rule = (char *) lua_tostring(ls, 2);
|
||||
const char *source = (char *) lua_tostring(ls, 3);
|
||||
const char *level = (char *) lua_tostring(ls, 4);
|
||||
const char *format = (char *) lua_tostring(ls, 5);
|
||||
|
||||
string sformat = format;
|
||||
|
||||
if(strcmp(source, "syscall") == 0)
|
||||
if(strcmp(source.c_str(), "syscall") == 0)
|
||||
{
|
||||
try {
|
||||
// This is "output"
|
||||
s_formatters->tostring((sinsp_evt *) evt, sformat, &line);
|
||||
// This is "output"
|
||||
s_formatters->tostring((sinsp_evt *)evt, sformat, &line);
|
||||
|
||||
if(s_json_output)
|
||||
{
|
||||
sinsp_evt::param_fmt cur_fmt = s_inspector->get_buffer_format();
|
||||
switch(cur_fmt)
|
||||
{
|
||||
case sinsp_evt::PF_NORMAL:
|
||||
s_inspector->set_buffer_format(sinsp_evt::PF_JSON);
|
||||
break;
|
||||
case sinsp_evt::PF_EOLS:
|
||||
s_inspector->set_buffer_format(sinsp_evt::PF_JSONEOLS);
|
||||
break;
|
||||
case sinsp_evt::PF_HEX:
|
||||
s_inspector->set_buffer_format(sinsp_evt::PF_JSONHEX);
|
||||
break;
|
||||
case sinsp_evt::PF_HEXASCII:
|
||||
s_inspector->set_buffer_format(sinsp_evt::PF_JSONHEXASCII);
|
||||
break;
|
||||
case sinsp_evt::PF_BASE64:
|
||||
s_inspector->set_buffer_format(sinsp_evt::PF_JSONBASE64);
|
||||
break;
|
||||
default:
|
||||
// do nothing
|
||||
break;
|
||||
}
|
||||
// This is output fields
|
||||
s_formatters->tostring((sinsp_evt *) evt, sformat, &json_line);
|
||||
|
||||
// The formatted string might have a leading newline. If it does, remove it.
|
||||
if (json_line[0] == '\n')
|
||||
{
|
||||
json_line.erase(0, 1);
|
||||
}
|
||||
s_inspector->set_buffer_format(cur_fmt);
|
||||
}
|
||||
}
|
||||
catch (sinsp_exception& e)
|
||||
if(s_json_output)
|
||||
{
|
||||
string err = "Invalid output format '" + sformat + "': '" + string(e.what()) + "'";
|
||||
lua_pushstring(ls, err.c_str());
|
||||
lua_error(ls);
|
||||
sinsp_evt::param_fmt cur_fmt = s_inspector->get_buffer_format();
|
||||
switch(cur_fmt)
|
||||
{
|
||||
case sinsp_evt::PF_NORMAL:
|
||||
s_inspector->set_buffer_format(sinsp_evt::PF_JSON);
|
||||
break;
|
||||
case sinsp_evt::PF_EOLS:
|
||||
s_inspector->set_buffer_format(sinsp_evt::PF_JSONEOLS);
|
||||
break;
|
||||
case sinsp_evt::PF_HEX:
|
||||
s_inspector->set_buffer_format(sinsp_evt::PF_JSONHEX);
|
||||
break;
|
||||
case sinsp_evt::PF_HEXASCII:
|
||||
s_inspector->set_buffer_format(sinsp_evt::PF_JSONHEXASCII);
|
||||
break;
|
||||
case sinsp_evt::PF_BASE64:
|
||||
s_inspector->set_buffer_format(sinsp_evt::PF_JSONBASE64);
|
||||
break;
|
||||
default:
|
||||
// do nothing
|
||||
break;
|
||||
}
|
||||
// This is output fields
|
||||
s_formatters->tostring((sinsp_evt *)evt, sformat, &json_line);
|
||||
|
||||
// The formatted string might have a leading newline. If it does, remove it.
|
||||
if(json_line[0] == '\n')
|
||||
{
|
||||
json_line.erase(0, 1);
|
||||
}
|
||||
s_inspector->set_buffer_format(cur_fmt);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
try {
|
||||
json_event_formatter formatter(s_engine->json_factory(), sformat);
|
||||
|
||||
json_event_formatter formatter(s_engine->json_factory(), sformat);
|
||||
line = formatter.tostring((json_event *)evt);
|
||||
|
||||
line = formatter.tostring((json_event *) evt);
|
||||
|
||||
if(s_json_output)
|
||||
{
|
||||
json_line = formatter.tojson((json_event *) evt);
|
||||
}
|
||||
}
|
||||
catch (exception &e)
|
||||
if(s_json_output)
|
||||
{
|
||||
string err = "Invalid output format '" + sformat + "': '" + string(e.what()) + "'";
|
||||
lua_pushstring(ls, err.c_str());
|
||||
lua_error(ls);
|
||||
json_line = formatter.tojson((json_event *)evt);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,15 +171,16 @@ int falco_formats::format_event (lua_State *ls)
|
||||
// message as well as the event time in ns. Use this to build
|
||||
// a more detailed object containing the event time, rule,
|
||||
// severity, full output, and fields.
|
||||
if (s_json_output) {
|
||||
if(s_json_output)
|
||||
{
|
||||
Json::Value event;
|
||||
Json::FastWriter writer;
|
||||
string full_line;
|
||||
|
||||
// Convert the time-as-nanoseconds to a more json-friendly ISO8601.
|
||||
time_t evttime = evt->get_ts()/1000000000;
|
||||
time_t evttime = evt->get_ts() / 1000000000;
|
||||
char time_sec[20]; // sizeof "YYYY-MM-DDTHH:MM:SS"
|
||||
char time_ns[12]; // sizeof ".sssssssssZ"
|
||||
char time_ns[12]; // sizeof ".sssssssssZ"
|
||||
string iso8601evttime;
|
||||
|
||||
strftime(time_sec, sizeof(time_sec), "%FT%T", gmtime(&evttime));
|
||||
@@ -246,9 +201,9 @@ int falco_formats::format_event (lua_State *ls)
|
||||
|
||||
// Json::FastWriter may add a trailing newline. If it
|
||||
// does, remove it.
|
||||
if (full_line[full_line.length()-1] == '\n')
|
||||
if(full_line[full_line.length() - 1] == '\n')
|
||||
{
|
||||
full_line.resize(full_line.length()-1);
|
||||
full_line.resize(full_line.length() - 1);
|
||||
}
|
||||
|
||||
// Cheat-graft the output from the formatter into this
|
||||
@@ -261,24 +216,12 @@ int falco_formats::format_event (lua_State *ls)
|
||||
line = full_line;
|
||||
}
|
||||
|
||||
lua_pushstring(ls, line.c_str());
|
||||
return 1;
|
||||
return line.c_str();
|
||||
}
|
||||
|
||||
int falco_formats::resolve_tokens(lua_State *ls)
|
||||
map<string, string> falco_formats::resolve_tokens(const gen_event *evt, const std::string &source, const std::string &format)
|
||||
{
|
||||
if(!lua_isstring(ls, -1) ||
|
||||
!lua_isstring(ls, -2) ||
|
||||
!lua_islightuserdata(ls, -3))
|
||||
{
|
||||
lua_pushstring(ls, "Invalid arguments passed to resolve_tokens()");
|
||||
lua_error(ls);
|
||||
}
|
||||
gen_event *evt = (gen_event *)lua_topointer(ls, 1);
|
||||
string source = luaL_checkstring(ls, 2);
|
||||
const char *format = (char *)lua_tostring(ls, 3);
|
||||
string sformat = format;
|
||||
|
||||
map<string, string> values;
|
||||
if(source == "syscall")
|
||||
{
|
||||
@@ -288,16 +231,7 @@ int falco_formats::resolve_tokens(lua_State *ls)
|
||||
else
|
||||
{
|
||||
json_event_formatter json_formatter(s_engine->json_factory(), sformat);
|
||||
values = json_formatter.tomap((json_event*) evt);
|
||||
values = json_formatter.tomap((json_event *)evt);
|
||||
}
|
||||
|
||||
lua_newtable(ls);
|
||||
for(auto const& v : values)
|
||||
{
|
||||
lua_pushstring(ls, v.first.c_str());
|
||||
lua_pushstring(ls, v.second.c_str());
|
||||
lua_settable(ls, -3);
|
||||
}
|
||||
|
||||
return 1;
|
||||
return values;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (C) 2019 The Falco Authors.
|
||||
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.
|
||||
@@ -18,7 +18,8 @@ limitations under the License.
|
||||
|
||||
#include "sinsp.h"
|
||||
|
||||
extern "C" {
|
||||
extern "C"
|
||||
{
|
||||
#include "lua.h"
|
||||
#include "lualib.h"
|
||||
#include "lauxlib.h"
|
||||
@@ -31,31 +32,28 @@ class sinsp_evt_formatter;
|
||||
|
||||
class falco_formats
|
||||
{
|
||||
public:
|
||||
static void init(sinsp* inspector,
|
||||
public:
|
||||
static void init(sinsp *inspector,
|
||||
falco_engine *engine,
|
||||
lua_State *ls,
|
||||
bool json_output,
|
||||
bool json_include_output_property);
|
||||
|
||||
// formatter = falco.formatter(format_string)
|
||||
static int formatter(lua_State *ls);
|
||||
static int lua_formatter(lua_State *ls);
|
||||
|
||||
// falco.free_formatter(formatter)
|
||||
static int free_formatter(lua_State *ls);
|
||||
static int lua_free_formatter(lua_State *ls);
|
||||
|
||||
// falco.free_formatters()
|
||||
static int free_formatters(lua_State *ls);
|
||||
static string format_event(const gen_event *evt, const std::string &rule, const std::string &source,
|
||||
const std::string &level, const std::string &format);
|
||||
|
||||
// formatted_string = falco.format_event(evt, formatter)
|
||||
static int format_event(lua_State *ls);
|
||||
static map<string, string> resolve_tokens(const gen_event *evt, const std::string &source,
|
||||
const std::string &format);
|
||||
|
||||
// resolve_tokens = falco.resolve_tokens(evt, formatter)
|
||||
static int resolve_tokens(lua_State *ls);
|
||||
|
||||
static sinsp* s_inspector;
|
||||
static sinsp *s_inspector;
|
||||
static falco_engine *s_engine;
|
||||
static sinsp_evt_formatter_cache *s_formatters;
|
||||
static std::unique_ptr<sinsp_evt_formatter_cache> s_formatters;
|
||||
static bool s_json_output;
|
||||
static bool s_json_include_output_property;
|
||||
};
|
||||
|
||||
@@ -47,6 +47,10 @@ add_executable(
|
||||
configuration.cpp
|
||||
logger.cpp
|
||||
falco_outputs.cpp
|
||||
outputs_file.cpp
|
||||
outputs_program.cpp
|
||||
outputs_stdout.cpp
|
||||
outputs_syslog.cpp
|
||||
event_drops.cpp
|
||||
statsfilewriter.cpp
|
||||
falco.cpp
|
||||
@@ -57,6 +61,12 @@ else()
|
||||
configuration.cpp
|
||||
logger.cpp
|
||||
falco_outputs.cpp
|
||||
outputs_file.cpp
|
||||
outputs_grpc.cpp
|
||||
outputs_http.cpp
|
||||
outputs_program.cpp
|
||||
outputs_stdout.cpp
|
||||
outputs_syslog.cpp
|
||||
event_drops.cpp
|
||||
statsfilewriter.cpp
|
||||
falco.cpp
|
||||
@@ -136,11 +146,11 @@ endif()
|
||||
configure_file(config_falco.h.in config_falco.h)
|
||||
|
||||
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")
|
||||
# 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()
|
||||
@@ -155,8 +165,3 @@ if(MUSL_OPTIMIZED_BUILD AND CMAKE_BUILD_TYPE STREQUAL "release")
|
||||
endif()
|
||||
|
||||
install(TARGETS falco DESTINATION ${FALCO_BIN_DIR})
|
||||
install(
|
||||
DIRECTORY lua
|
||||
DESTINATION ${FALCO_SHARE_DIR}
|
||||
FILES_MATCHING
|
||||
PATTERN *.lua)
|
||||
|
||||
@@ -25,11 +25,9 @@ limitations under the License.
|
||||
#define FALCO_VERSION_PRERELEASE "@FALCO_VERSION_PRERELEASE@"
|
||||
#define FALCO_VERSION_BUILD "@FALCO_VERSION_BUILD@"
|
||||
|
||||
#define FALCO_LUA_DIR "${CMAKE_INSTALL_PREFIX}/${FALCO_SHARE_DIR}/lua/"
|
||||
#define FALCO_SOURCE_DIR "${PROJECT_SOURCE_DIR}"
|
||||
#define FALCO_SOURCE_CONF_FILE "${PROJECT_SOURCE_DIR}/falco.yaml"
|
||||
#define FALCO_INSTALL_CONF_FILE "/etc/falco/falco.yaml"
|
||||
#define FALCO_SOURCE_LUA_DIR "${PROJECT_SOURCE_DIR}/userspace/falco/lua/"
|
||||
|
||||
#define PROBE_NAME "@PROBE_NAME@"
|
||||
#define DRIVER_VERSION "@PROBE_VERSION@"
|
||||
@@ -52,7 +52,7 @@ void falco_configuration::init(list<string> &cmdline_options)
|
||||
{
|
||||
init_cmdline_options(cmdline_options);
|
||||
|
||||
falco_outputs::output_config stdout_output;
|
||||
falco::outputs::config stdout_output;
|
||||
stdout_output.name = "stdout";
|
||||
m_outputs.push_back(stdout_output);
|
||||
}
|
||||
@@ -68,20 +68,10 @@ void falco_configuration::init(string conf_filename, list<string> &cmdline_optio
|
||||
|
||||
m_config->get_sequence<list<string>>(rules_files, string("rules_file"));
|
||||
|
||||
for(auto &file : rules_files)
|
||||
{
|
||||
// Here, we only include files that exist
|
||||
struct stat buffer;
|
||||
if(stat(file.c_str(), &buffer) == 0)
|
||||
{
|
||||
read_rules_file_directory(file, m_rules_filenames);
|
||||
}
|
||||
}
|
||||
|
||||
m_json_output = m_config->get_scalar<bool>("json_output", false);
|
||||
m_json_include_output_property = m_config->get_scalar<bool>("json_include_output_property", true);
|
||||
|
||||
falco_outputs::output_config file_output;
|
||||
falco::outputs::config file_output;
|
||||
file_output.name = "file";
|
||||
if(m_config->get_scalar<bool>("file_output", "enabled", false))
|
||||
{
|
||||
@@ -99,21 +89,21 @@ void falco_configuration::init(string conf_filename, list<string> &cmdline_optio
|
||||
m_outputs.push_back(file_output);
|
||||
}
|
||||
|
||||
falco_outputs::output_config stdout_output;
|
||||
falco::outputs::config stdout_output;
|
||||
stdout_output.name = "stdout";
|
||||
if(m_config->get_scalar<bool>("stdout_output", "enabled", false))
|
||||
{
|
||||
m_outputs.push_back(stdout_output);
|
||||
}
|
||||
|
||||
falco_outputs::output_config syslog_output;
|
||||
falco::outputs::config syslog_output;
|
||||
syslog_output.name = "syslog";
|
||||
if(m_config->get_scalar<bool>("syslog_output", "enabled", false))
|
||||
{
|
||||
m_outputs.push_back(syslog_output);
|
||||
}
|
||||
|
||||
falco_outputs::output_config program_output;
|
||||
falco::outputs::config program_output;
|
||||
program_output.name = "program";
|
||||
if(m_config->get_scalar<bool>("program_output", "enabled", false))
|
||||
{
|
||||
@@ -131,7 +121,7 @@ void falco_configuration::init(string conf_filename, list<string> &cmdline_optio
|
||||
m_outputs.push_back(program_output);
|
||||
}
|
||||
|
||||
falco_outputs::output_config http_output;
|
||||
falco::outputs::config http_output;
|
||||
http_output.name = "http";
|
||||
if(m_config->get_scalar<bool>("http_output", "enabled", false))
|
||||
{
|
||||
@@ -159,7 +149,7 @@ void falco_configuration::init(string conf_filename, list<string> &cmdline_optio
|
||||
m_grpc_cert_chain = m_config->get_scalar<string>("grpc", "cert_chain", "/etc/falco/certs/server.crt");
|
||||
m_grpc_root_certs = m_config->get_scalar<string>("grpc", "root_certs", "/etc/falco/certs/ca.crt");
|
||||
|
||||
falco_outputs::output_config grpc_output;
|
||||
falco::outputs::config grpc_output;
|
||||
grpc_output.name = "grpc";
|
||||
// gRPC output is enabled only if gRPC server is enabled too
|
||||
if(m_config->get_scalar<bool>("grpc_output", "enabled", true) && m_grpc_enabled)
|
||||
@@ -242,69 +232,6 @@ void falco_configuration::init(string conf_filename, list<string> &cmdline_optio
|
||||
m_syscall_evt_simulate_drops = m_config->get_scalar<bool>("syscall_event_drops", "simulate_drops", false);
|
||||
}
|
||||
|
||||
void falco_configuration::read_rules_file_directory(const string &path, list<string> &rules_filenames)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
int rc = stat(path.c_str(), &st);
|
||||
|
||||
if(rc != 0)
|
||||
{
|
||||
std::cerr << "Could not get info on rules file " << path << ": " << strerror(errno) << std::endl;
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if(st.st_mode & S_IFDIR)
|
||||
{
|
||||
// It's a directory. Read the contents, sort
|
||||
// alphabetically, and add every path to
|
||||
// rules_filenames
|
||||
vector<string> dir_filenames;
|
||||
|
||||
DIR *dir = opendir(path.c_str());
|
||||
|
||||
if(!dir)
|
||||
{
|
||||
std::cerr << "Could not get read contents of directory " << path << ": " << strerror(errno) << std::endl;
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
for(struct dirent *ent = readdir(dir); ent; ent = readdir(dir))
|
||||
{
|
||||
string efile = path + "/" + ent->d_name;
|
||||
|
||||
rc = stat(efile.c_str(), &st);
|
||||
|
||||
if(rc != 0)
|
||||
{
|
||||
std::cerr << "Could not get info on rules file " << efile << ": " << strerror(errno) << std::endl;
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if(st.st_mode & S_IFREG)
|
||||
{
|
||||
dir_filenames.push_back(efile);
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
|
||||
std::sort(dir_filenames.begin(),
|
||||
dir_filenames.end());
|
||||
|
||||
for(string &ent : dir_filenames)
|
||||
{
|
||||
rules_filenames.push_back(ent);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Assume it's a file and just add to
|
||||
// rules_filenames. If it can't be opened/etc that
|
||||
// will be reported later..
|
||||
rules_filenames.push_back(path);
|
||||
}
|
||||
}
|
||||
|
||||
static bool split(const string &str, char delim, pair<string, string> &parts)
|
||||
{
|
||||
@@ -346,4 +273,4 @@ void falco_configuration::set_cmdline_option(const string &opt)
|
||||
{
|
||||
m_config->set_scalar(keyval.first, keyval.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ public:
|
||||
{
|
||||
m_path = path;
|
||||
YAML::Node config;
|
||||
std::vector<falco_outputs::output_config> outputs;
|
||||
std::vector<falco::outputs::config> outputs;
|
||||
try
|
||||
{
|
||||
m_root = YAML::LoadFile(path);
|
||||
@@ -190,13 +190,10 @@ public:
|
||||
void init(std::string conf_filename, std::list<std::string>& cmdline_options);
|
||||
void init(std::list<std::string>& cmdline_options);
|
||||
|
||||
static void read_rules_file_directory(const string& path, list<string>& rules_filenames);
|
||||
|
||||
std::list<std::string> m_rules_filenames;
|
||||
bool m_json_output;
|
||||
bool m_json_include_output_property;
|
||||
std::string m_log_level;
|
||||
std::vector<falco_outputs::output_config> m_outputs;
|
||||
std::vector<falco::outputs::config> m_outputs;
|
||||
uint32_t m_notifications_rate;
|
||||
uint32_t m_notifications_max_burst;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (C) 2019 The Falco Authors.
|
||||
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.
|
||||
@@ -15,6 +15,7 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
#include "event_drops.h"
|
||||
#include "falco_common.h"
|
||||
#include "banned.h" // This raises a compilation error when certain functions are used
|
||||
|
||||
syscall_evt_drop_mgr::syscall_evt_drop_mgr():
|
||||
@@ -137,7 +138,7 @@ bool syscall_evt_drop_mgr::perform_actions(uint64_t now, scap_stats &delta, bool
|
||||
|
||||
case ACT_ALERT:
|
||||
m_outputs->handle_msg(now,
|
||||
falco_outputs::PRIORITY_CRITICAL,
|
||||
falco_common::PRIORITY_CRITICAL,
|
||||
msg,
|
||||
rule,
|
||||
output_fields);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (C) 2019 The Falco Authors.
|
||||
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.
|
||||
@@ -24,24 +24,21 @@ limitations under the License.
|
||||
|
||||
#include "formats.h"
|
||||
#include "logger.h"
|
||||
|
||||
#include "outputs_file.h"
|
||||
#include "outputs_program.h"
|
||||
#include "outputs_stdout.h"
|
||||
#include "outputs_syslog.h"
|
||||
#ifndef MINIMAL_BUILD
|
||||
#include "falco_outputs_queue.h"
|
||||
#include "outputs_http.h"
|
||||
#include "outputs_grpc.h"
|
||||
#endif
|
||||
|
||||
#include "banned.h" // This raises a compilation error when certain functions are used
|
||||
|
||||
using namespace std;
|
||||
|
||||
const static struct luaL_reg ll_falco_outputs [] =
|
||||
{
|
||||
#ifndef MINIMAL_BUILD
|
||||
{"handle_http", &falco_outputs::handle_http},
|
||||
{"handle_grpc", &falco_outputs::handle_grpc},
|
||||
#endif
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
falco_outputs::falco_outputs(falco_engine *engine):
|
||||
m_falco_engine(engine),
|
||||
falco_outputs::falco_outputs():
|
||||
m_initialized(false),
|
||||
m_buffered(true),
|
||||
m_json_output(false),
|
||||
@@ -52,25 +49,11 @@ falco_outputs::falco_outputs(falco_engine *engine):
|
||||
|
||||
falco_outputs::~falco_outputs()
|
||||
{
|
||||
// Note: The assert()s in this destructor were previously places where
|
||||
// exceptions were thrown. C++11 doesn't allow destructors to
|
||||
// emit exceptions; if they're thrown, they'll trigger a call
|
||||
// to 'terminate()'. To maintain similar behavior, the exceptions
|
||||
// were replace with calls to 'assert()'
|
||||
if(m_initialized)
|
||||
{
|
||||
lua_getglobal(m_ls, m_lua_output_cleanup.c_str());
|
||||
if(!lua_isfunction(m_ls, -1))
|
||||
for(auto it = m_outputs.cbegin(); it != m_outputs.cend(); ++it)
|
||||
{
|
||||
falco_logger::log(LOG_ERR, std::string("No function ") + m_lua_output_cleanup + " found. ");
|
||||
assert(nullptr == "Missing lua cleanup function in ~falco_outputs");
|
||||
}
|
||||
|
||||
if(lua_pcall(m_ls, 0, 0, 0) != 0)
|
||||
{
|
||||
const char *lerr = lua_tostring(m_ls, -1);
|
||||
falco_logger::log(LOG_ERR, std::string("lua_pcall failed, err: ") + lerr);
|
||||
assert(nullptr == "lua_pcall failed in ~falco_outputs");
|
||||
(*it)->cleanup();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -78,27 +61,15 @@ falco_outputs::~falco_outputs()
|
||||
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,
|
||||
const string& alternate_lua_dir)
|
||||
bool time_format_iso_8601, string hostname)
|
||||
{
|
||||
// The engine must have been given an inspector by now.
|
||||
if(!m_inspector)
|
||||
{
|
||||
throw falco_exception("No inspector provided");
|
||||
}
|
||||
|
||||
m_json_output = json_output;
|
||||
|
||||
falco_common::init(m_lua_main_filename.c_str(), alternate_lua_dir.c_str());
|
||||
|
||||
// Note that falco_formats is added to both the lua state used
|
||||
// by the falco engine as well as the separate lua state used
|
||||
// by falco outputs.
|
||||
falco_formats::init(m_inspector, m_falco_engine, m_ls, json_output, json_include_output_property);
|
||||
|
||||
falco_logger::init(m_ls);
|
||||
|
||||
luaL_openlib(m_ls, "c_outputs", ll_falco_outputs, 0);
|
||||
// Note that falco_formats is already initialized by the engine,
|
||||
// and the following json options are not used within the engine.
|
||||
// So we can safely update them.
|
||||
falco_formats::s_json_output = json_output;
|
||||
falco_formats::s_json_include_output_property = json_include_output_property;
|
||||
|
||||
m_notifications_tb.init(rate, max_burst);
|
||||
|
||||
@@ -109,40 +80,47 @@ void falco_outputs::init(bool json_output,
|
||||
m_initialized = true;
|
||||
}
|
||||
|
||||
void falco_outputs::add_output(output_config oc)
|
||||
void falco_outputs::add_output(falco::outputs::config oc)
|
||||
{
|
||||
uint8_t nargs = 3;
|
||||
lua_getglobal(m_ls, m_lua_add_output.c_str());
|
||||
|
||||
if(!lua_isfunction(m_ls, -1))
|
||||
falco::outputs::abstract_output *oo;
|
||||
|
||||
if(oc.name == "file")
|
||||
{
|
||||
throw falco_exception("No function " + m_lua_add_output + " found. ");
|
||||
oo = new falco::outputs::output_file();
|
||||
}
|
||||
lua_pushstring(m_ls, oc.name.c_str());
|
||||
lua_pushnumber(m_ls, (m_buffered ? 1 : 0));
|
||||
lua_pushnumber(m_ls, (m_time_format_iso_8601 ? 1 : 0));
|
||||
|
||||
// If we have options, build up a lua table containing them
|
||||
if(oc.options.size())
|
||||
else if(oc.name == "program")
|
||||
{
|
||||
nargs = 4;
|
||||
lua_createtable(m_ls, 0, oc.options.size());
|
||||
|
||||
for(auto it = oc.options.cbegin(); it != oc.options.cend(); ++it)
|
||||
{
|
||||
lua_pushstring(m_ls, (*it).second.c_str());
|
||||
lua_setfield(m_ls, -2, (*it).first.c_str());
|
||||
}
|
||||
oo = new falco::outputs::output_program();
|
||||
}
|
||||
else if(oc.name == "stdout")
|
||||
{
|
||||
oo = new falco::outputs::output_stdout();
|
||||
}
|
||||
else if(oc.name == "syslog")
|
||||
{
|
||||
oo = new falco::outputs::output_syslog();
|
||||
}
|
||||
#ifndef MINIMAL_BUILD
|
||||
else if(oc.name == "http")
|
||||
{
|
||||
oo = new falco::outputs::output_http();
|
||||
}
|
||||
else if(oc.name == "grpc")
|
||||
{
|
||||
oo = new falco::outputs::output_grpc();
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
throw falco_exception("Output not supported: " + oc.name);
|
||||
}
|
||||
|
||||
if(lua_pcall(m_ls, nargs, 0, 0) != 0)
|
||||
{
|
||||
const char *lerr = lua_tostring(m_ls, -1);
|
||||
throw falco_exception(string(lerr));
|
||||
}
|
||||
oo->init(oc, m_buffered, m_hostname);
|
||||
m_outputs.push_back(oo);
|
||||
}
|
||||
|
||||
void falco_outputs::handle_event(gen_event *ev, string &rule, string &source,
|
||||
void falco_outputs::handle_event(gen_event *evt, string &rule, string &source,
|
||||
falco_common::priority_type priority, string &format)
|
||||
{
|
||||
if(!m_notifications_tb.claim())
|
||||
@@ -151,29 +129,46 @@ void falco_outputs::handle_event(gen_event *ev, string &rule, string &source,
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> guard(m_ls_semaphore);
|
||||
lua_getglobal(m_ls, m_lua_output_event.c_str());
|
||||
|
||||
if(lua_isfunction(m_ls, -1))
|
||||
string sformat;
|
||||
if(source == "syscall")
|
||||
{
|
||||
lua_pushlightuserdata(m_ls, ev);
|
||||
lua_pushstring(m_ls, rule.c_str());
|
||||
lua_pushstring(m_ls, source.c_str());
|
||||
lua_pushstring(m_ls, falco_common::priority_names[priority].c_str());
|
||||
lua_pushnumber(m_ls, priority);
|
||||
lua_pushstring(m_ls, format.c_str());
|
||||
lua_pushstring(m_ls, m_hostname.c_str());
|
||||
|
||||
if(lua_pcall(m_ls, 7, 0, 0) != 0)
|
||||
if(m_time_format_iso_8601)
|
||||
{
|
||||
const char *lerr = lua_tostring(m_ls, -1);
|
||||
string err = "Error invoking function output: " + string(lerr);
|
||||
throw falco_exception(err);
|
||||
sformat = "*%evt.time.iso8601: " + falco_common::priority_names[priority];
|
||||
}
|
||||
else
|
||||
{
|
||||
sformat = "*%evt.time: " + falco_common::priority_names[priority];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw falco_exception("No function " + m_lua_output_event + " found in lua compiler module");
|
||||
if(m_time_format_iso_8601)
|
||||
{
|
||||
sformat = "*%jevt.time.iso8601: " + falco_common::priority_names[priority];
|
||||
}
|
||||
else
|
||||
{
|
||||
sformat = "*%jevt.time: " + falco_common::priority_names[priority];
|
||||
}
|
||||
}
|
||||
|
||||
// if format starts with a *, remove it, as we added our own prefix
|
||||
if(format[0] == '*')
|
||||
{
|
||||
sformat += " " + format.substr(1, format.length() - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
sformat += " " + format;
|
||||
}
|
||||
|
||||
string msg;
|
||||
msg = falco_formats::format_event(evt, rule, source, falco_common::priority_names[priority], sformat);
|
||||
|
||||
for(auto it = m_outputs.cbegin(); it != m_outputs.cend(); ++it)
|
||||
{
|
||||
(*it)->output_event(evt, rule, source, priority, sformat, msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,7 +196,7 @@ void falco_outputs::handle_msg(uint64_t now,
|
||||
iso8601evttime += time_ns;
|
||||
|
||||
jmsg["output"] = msg;
|
||||
jmsg["priority"] = "Critical";
|
||||
jmsg["priority"] = falco_common::priority_names[priority];
|
||||
jmsg["rule"] = rule;
|
||||
jmsg["time"] = iso8601evttime;
|
||||
jmsg["output_fields"] = output_fields;
|
||||
@@ -214,7 +209,7 @@ void falco_outputs::handle_msg(uint64_t now,
|
||||
bool first = true;
|
||||
|
||||
sinsp_utils::ts_to_string(now, ×tr, false, true);
|
||||
full_msg = timestr + ": " + falco_common::priority_names[LOG_CRIT] + " " + msg + " (";
|
||||
full_msg = timestr + ": " + falco_common::priority_names[priority] + " " + msg + " (";
|
||||
for(auto &pair : output_fields)
|
||||
{
|
||||
if(first)
|
||||
@@ -230,151 +225,16 @@ void falco_outputs::handle_msg(uint64_t now,
|
||||
full_msg += ")";
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> guard(m_ls_semaphore);
|
||||
lua_getglobal(m_ls, m_lua_output_msg.c_str());
|
||||
if(lua_isfunction(m_ls, -1))
|
||||
for(auto it = m_outputs.cbegin(); it != m_outputs.cend(); ++it)
|
||||
{
|
||||
lua_pushstring(m_ls, full_msg.c_str());
|
||||
lua_pushstring(m_ls, falco_common::priority_names[priority].c_str());
|
||||
lua_pushnumber(m_ls, priority);
|
||||
|
||||
if(lua_pcall(m_ls, 3, 0, 0) != 0)
|
||||
{
|
||||
const char *lerr = lua_tostring(m_ls, -1);
|
||||
string err = "Error invoking function output: " + string(lerr);
|
||||
throw falco_exception(err);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw falco_exception("No function " + m_lua_output_msg + " found in lua compiler module");
|
||||
(*it)->output_msg(priority, full_msg);
|
||||
}
|
||||
}
|
||||
|
||||
void falco_outputs::reopen_outputs()
|
||||
{
|
||||
lua_getglobal(m_ls, m_lua_output_reopen.c_str());
|
||||
if(!lua_isfunction(m_ls, -1))
|
||||
for(auto it = m_outputs.cbegin(); it != m_outputs.cend(); ++it)
|
||||
{
|
||||
throw falco_exception("No function " + m_lua_output_reopen + " found. ");
|
||||
}
|
||||
|
||||
if(lua_pcall(m_ls, 0, 0, 0) != 0)
|
||||
{
|
||||
const char *lerr = lua_tostring(m_ls, -1);
|
||||
throw falco_exception(string(lerr));
|
||||
(*it)->reopen();
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef MINIMAL_BUILD
|
||||
int falco_outputs::handle_http(lua_State *ls)
|
||||
{
|
||||
CURL *curl = NULL;
|
||||
CURLcode res = CURLE_FAILED_INIT;
|
||||
struct curl_slist *slist1;
|
||||
slist1 = NULL;
|
||||
|
||||
if(!lua_isstring(ls, -1) ||
|
||||
!lua_isstring(ls, -2))
|
||||
{
|
||||
lua_pushstring(ls, "Invalid arguments passed to handle_http()");
|
||||
lua_error(ls);
|
||||
}
|
||||
|
||||
string url = (char *)lua_tostring(ls, 1);
|
||||
string msg = (char *)lua_tostring(ls, 2);
|
||||
|
||||
curl = curl_easy_init();
|
||||
if(curl)
|
||||
{
|
||||
slist1 = curl_slist_append(slist1, "Content-Type: application/json");
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist1);
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, msg.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, -1L);
|
||||
|
||||
res = curl_easy_perform(curl);
|
||||
|
||||
if(res != CURLE_OK)
|
||||
{
|
||||
falco_logger::log(LOG_ERR, "libcurl error: " + string(curl_easy_strerror(res)));
|
||||
}
|
||||
curl_easy_cleanup(curl);
|
||||
curl = NULL;
|
||||
curl_slist_free_all(slist1);
|
||||
slist1 = NULL;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int falco_outputs::handle_grpc(lua_State *ls)
|
||||
{
|
||||
// check parameters
|
||||
if(!lua_islightuserdata(ls, -8) ||
|
||||
!lua_isstring(ls, -7) ||
|
||||
!lua_isstring(ls, -6) ||
|
||||
!lua_isstring(ls, -5) ||
|
||||
!lua_isstring(ls, -4) ||
|
||||
!lua_istable(ls, -3) ||
|
||||
!lua_isstring(ls, -2) ||
|
||||
!lua_istable(ls, -1))
|
||||
{
|
||||
lua_pushstring(ls, "Invalid arguments passed to handle_grpc()");
|
||||
lua_error(ls);
|
||||
}
|
||||
|
||||
falco::outputs::response grpc_res;
|
||||
|
||||
// time
|
||||
gen_event *evt = (gen_event *)lua_topointer(ls, 1);
|
||||
auto timestamp = grpc_res.mutable_time();
|
||||
*timestamp = google::protobuf::util::TimeUtil::NanosecondsToTimestamp(evt->get_ts());
|
||||
|
||||
// rule
|
||||
auto rule = grpc_res.mutable_rule();
|
||||
*rule = (char *)lua_tostring(ls, 2);
|
||||
|
||||
// source
|
||||
falco::schema::source s = falco::schema::source::SYSCALL;
|
||||
string sstr = (char *)lua_tostring(ls, 3);
|
||||
if(!falco::schema::source_Parse(sstr, &s))
|
||||
{
|
||||
lua_pushstring(ls, "Unknown source passed to to handle_grpc()");
|
||||
lua_error(ls);
|
||||
}
|
||||
grpc_res.set_source(s);
|
||||
|
||||
// priority
|
||||
falco::schema::priority p = falco::schema::priority::EMERGENCY;
|
||||
string pstr = (char *)lua_tostring(ls, 4);
|
||||
if(!falco::schema::priority_Parse(pstr, &p))
|
||||
{
|
||||
lua_pushstring(ls, "Unknown priority passed to to handle_grpc()");
|
||||
lua_error(ls);
|
||||
}
|
||||
grpc_res.set_priority(p);
|
||||
|
||||
// output
|
||||
auto output = grpc_res.mutable_output();
|
||||
*output = (char *)lua_tostring(ls, 5);
|
||||
|
||||
// output fields
|
||||
auto &fields = *grpc_res.mutable_output_fields();
|
||||
|
||||
lua_pushnil(ls); // so that lua_next removes it from stack and puts (k, v) on it
|
||||
while(lua_next(ls, 6) != 0)
|
||||
{
|
||||
fields[lua_tostring(ls, -2)] = lua_tostring(ls, -1);
|
||||
lua_pop(ls, 1); // remove value, keep key for lua_next
|
||||
}
|
||||
lua_pop(ls, 1); // pop table
|
||||
|
||||
// hostname
|
||||
auto host = grpc_res.mutable_hostname();
|
||||
*host = (char *)lua_tostring(ls, 7);
|
||||
|
||||
falco::outputs::queue::get().push(grpc_res);
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (C) 2019 The Falco Authors.
|
||||
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.
|
||||
@@ -19,51 +19,36 @@ limitations under the License.
|
||||
#include <memory>
|
||||
#include <map>
|
||||
|
||||
extern "C" {
|
||||
#include "lua.h"
|
||||
#include "lualib.h"
|
||||
#include "lauxlib.h"
|
||||
}
|
||||
|
||||
#include "gen_filter.h"
|
||||
#include "json_evt.h"
|
||||
#include "falco_common.h"
|
||||
#include "token_bucket.h"
|
||||
#include "falco_engine.h"
|
||||
#include "outputs.h"
|
||||
|
||||
//
|
||||
// This class acts as the primary interface between a program and the
|
||||
// falco output engine. The falco rules engine is implemented by a
|
||||
// separate class falco_engine.
|
||||
//
|
||||
|
||||
class falco_outputs : public falco_common
|
||||
class falco_outputs
|
||||
{
|
||||
public:
|
||||
falco_outputs(falco_engine *engine);
|
||||
falco_outputs();
|
||||
virtual ~falco_outputs();
|
||||
|
||||
// The way to refer to an output (file, syslog, stdout, etc.)
|
||||
// An output has a name and set of options.
|
||||
struct output_config
|
||||
{
|
||||
std::string name;
|
||||
std::map<std::string, std::string> options;
|
||||
};
|
||||
|
||||
void init(bool json_output,
|
||||
bool json_include_output_property,
|
||||
uint32_t rate, uint32_t max_burst, bool buffered,
|
||||
bool time_format_iso_8601, std::string hostname,
|
||||
const std::string& alternate_lua_dir);
|
||||
bool time_format_iso_8601, std::string hostname);
|
||||
|
||||
void add_output(output_config oc);
|
||||
void add_output(falco::outputs::config oc);
|
||||
|
||||
//
|
||||
// ev is an event that has matched some rule. Pass the event
|
||||
// evt is an event that has matched some rule. Pass the event
|
||||
// to all configured outputs.
|
||||
//
|
||||
void handle_event(gen_event *ev, std::string &rule, std::string &source,
|
||||
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.
|
||||
@@ -71,21 +56,15 @@ public:
|
||||
falco_common::priority_type priority,
|
||||
std::string &msg,
|
||||
std::string &rule,
|
||||
std::map<std::string,std::string> &output_fields);
|
||||
std::map<std::string, std::string> &output_fields);
|
||||
|
||||
void reopen_outputs();
|
||||
|
||||
#ifndef MINIMAL_BUILD
|
||||
static int handle_http(lua_State *ls);
|
||||
static int handle_grpc(lua_State *ls);
|
||||
#endif
|
||||
|
||||
private:
|
||||
|
||||
falco_engine *m_falco_engine;
|
||||
|
||||
bool m_initialized;
|
||||
|
||||
std::vector<falco::outputs::abstract_output *> m_outputs;
|
||||
|
||||
// Rate limits notifications
|
||||
token_bucket m_notifications_tb;
|
||||
|
||||
@@ -93,11 +72,4 @@ private:
|
||||
bool m_json_output;
|
||||
bool m_time_format_iso_8601;
|
||||
std::string m_hostname;
|
||||
|
||||
std::string m_lua_add_output = "add_output";
|
||||
std::string m_lua_output_event = "output_event";
|
||||
std::string m_lua_output_msg = "output_msg";
|
||||
std::string m_lua_output_cleanup = "output_cleanup";
|
||||
std::string m_lua_output_reopen = "output_reopen";
|
||||
std::string m_lua_main_filename = "output.lua";
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (C) 2019 The Falco Authors
|
||||
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.
|
||||
@@ -21,9 +21,9 @@ limitations under the License.
|
||||
|
||||
namespace falco
|
||||
{
|
||||
namespace outputs
|
||||
namespace grpc
|
||||
{
|
||||
typedef tbb::concurrent_queue<response> response_cq;
|
||||
typedef tbb::concurrent_queue<outputs::response> response_cq;
|
||||
|
||||
class queue
|
||||
{
|
||||
@@ -34,12 +34,12 @@ public:
|
||||
return instance;
|
||||
}
|
||||
|
||||
bool try_pop(response& res)
|
||||
bool try_pop(outputs::response& res)
|
||||
{
|
||||
return m_queue.try_pop(res);
|
||||
}
|
||||
|
||||
void push(response& res)
|
||||
void push(outputs::response& res)
|
||||
{
|
||||
m_queue.push(res);
|
||||
}
|
||||
@@ -56,5 +56,5 @@ public:
|
||||
queue(queue const&) = delete;
|
||||
void operator=(queue const&) = delete;
|
||||
};
|
||||
} // namespace output
|
||||
} // namespace grpc
|
||||
} // namespace falco
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (C) 2019 The Falco Authors
|
||||
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.
|
||||
@@ -16,7 +16,7 @@ limitations under the License.
|
||||
|
||||
#include "config_falco.h"
|
||||
#include "grpc_server_impl.h"
|
||||
#include "falco_outputs_queue.h"
|
||||
#include "grpc_queue.h"
|
||||
#include "logger.h"
|
||||
#include "banned.h" // This raises a compilation error when certain functions are used
|
||||
|
||||
@@ -44,7 +44,7 @@ void falco::grpc::server_impl::get(const stream_context& ctx, const outputs::req
|
||||
// m_status == stream_context::STREAMING?
|
||||
// todo(leodido) > set m_stream
|
||||
|
||||
ctx.m_has_more = outputs::queue::get().try_pop(res);
|
||||
ctx.m_has_more = queue::get().try_pop(res);
|
||||
}
|
||||
|
||||
void falco::grpc::server_impl::sub(const bidi_context& ctx, const outputs::request& req, outputs::response& res)
|
||||
@@ -61,7 +61,7 @@ void falco::grpc::server_impl::sub(const bidi_context& ctx, const outputs::reque
|
||||
// m_status == stream_context::STREAMING?
|
||||
// todo(leodido) > set m_stream
|
||||
|
||||
ctx.m_has_more = outputs::queue::get().try_pop(res);
|
||||
ctx.m_has_more = queue::get().try_pop(res);
|
||||
}
|
||||
|
||||
void falco::grpc::server_impl::version(const context& ctx, const version::request&, version::response& res)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (C) 2019 The Falco Authors.
|
||||
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.
|
||||
@@ -16,25 +16,13 @@ limitations under the License.
|
||||
|
||||
#include <ctime>
|
||||
#include "logger.h"
|
||||
#include "chisel_api.h"
|
||||
|
||||
#include "falco_common.h"
|
||||
#include "banned.h" // This raises a compilation error when certain functions are used
|
||||
|
||||
const static struct luaL_reg ll_falco [] =
|
||||
{
|
||||
{"syslog", &falco_logger::syslog},
|
||||
{NULL,NULL}
|
||||
};
|
||||
|
||||
int falco_logger::level = LOG_INFO;
|
||||
bool falco_logger::time_format_iso_8601 = false;
|
||||
|
||||
void falco_logger::init(lua_State *ls)
|
||||
{
|
||||
luaL_openlib(ls, "falco", ll_falco, 0);
|
||||
}
|
||||
|
||||
void falco_logger::set_time_format_iso_8601(bool val)
|
||||
{
|
||||
falco_logger::time_format_iso_8601 = val;
|
||||
@@ -81,19 +69,6 @@ void falco_logger::set_level(string &level)
|
||||
}
|
||||
|
||||
|
||||
int falco_logger::syslog(lua_State *ls) {
|
||||
int priority = luaL_checknumber(ls, 1);
|
||||
|
||||
if (priority > LOG_DEBUG) {
|
||||
return luaL_argerror(ls, 1, "falco.syslog: priority must be a number between 0 and 7");
|
||||
}
|
||||
|
||||
const char *msg = luaL_checkstring(ls, 2);
|
||||
::syslog(priority, "%s", msg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool falco_logger::log_stderr = true;
|
||||
bool falco_logger::log_syslog = true;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (C) 2019 The Falco Authors.
|
||||
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.
|
||||
@@ -19,25 +19,15 @@ limitations under the License.
|
||||
#include "sinsp.h"
|
||||
#include <syslog.h>
|
||||
|
||||
extern "C" {
|
||||
#include "lua.h"
|
||||
#include "lualib.h"
|
||||
#include "lauxlib.h"
|
||||
}
|
||||
|
||||
class falco_logger
|
||||
{
|
||||
public:
|
||||
static void init(lua_State *ls);
|
||||
|
||||
static void set_time_format_iso_8601(bool val);
|
||||
|
||||
// Will throw exception if level is unknown.
|
||||
static void set_level(string &level);
|
||||
|
||||
// value = falco.syslog(level, message)
|
||||
static int syslog(lua_State *ls);
|
||||
|
||||
static void log(int priority, const string msg);
|
||||
|
||||
static int level;
|
||||
|
||||
1
userspace/falco/lua/.gitignore
vendored
1
userspace/falco/lua/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
lyaml*
|
||||
@@ -1,271 +0,0 @@
|
||||
-- Copyright (C) 2019 The Falco Authors.
|
||||
--
|
||||
-- Licensed under the Apache License, Version 2.0 (the "License");
|
||||
-- you may not use this file except in compliance with the License.
|
||||
-- You may obtain a copy of the License at
|
||||
--
|
||||
-- http://www.apache.org/licenses/LICENSE-2.0
|
||||
--
|
||||
-- Unless required by applicable law or agreed to in writing, software
|
||||
-- distributed under the License is distributed on an "AS IS" BASIS,
|
||||
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
-- See the License for the specific language governing permissions and
|
||||
-- limitations under the License.
|
||||
--
|
||||
|
||||
local mod = {}
|
||||
|
||||
local outputs = {}
|
||||
|
||||
function mod.stdout(event, rule, source, priority, priority_num, msg, format, hostname, options)
|
||||
mod.stdout_message(priority, priority_num, msg, options)
|
||||
end
|
||||
|
||||
function mod.stdout_message(priority, priority_num, msg, options)
|
||||
if options.buffered == 0 then
|
||||
io.stdout:setvbuf "no"
|
||||
end
|
||||
print(msg)
|
||||
end
|
||||
|
||||
function mod.stdout_cleanup()
|
||||
io.stdout:flush()
|
||||
end
|
||||
|
||||
-- Note: not actually closing/reopening stdout
|
||||
function mod.stdout_reopen(options)
|
||||
end
|
||||
|
||||
function mod.file_validate(options)
|
||||
if (not type(options.filename) == "string") then
|
||||
error("File output needs to be configured with a valid filename")
|
||||
end
|
||||
|
||||
local file, err = io.open(options.filename, "a+")
|
||||
if file == nil then
|
||||
error("Error with file output: " .. err)
|
||||
end
|
||||
file:close()
|
||||
end
|
||||
|
||||
function mod.file_open(options)
|
||||
if ffile == nil then
|
||||
ffile = io.open(options.filename, "a+")
|
||||
if options.buffered == 0 then
|
||||
ffile:setvbuf "no"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function mod.file(event, rule, source, priority, priority_num, msg, format, hostname, options)
|
||||
mod.file_message(priority, priority_num, msg, options)
|
||||
end
|
||||
|
||||
function mod.file_message(priority, priority_num, msg, options)
|
||||
if options.keep_alive == "true" then
|
||||
mod.file_open(options)
|
||||
else
|
||||
ffile = io.open(options.filename, "a+")
|
||||
end
|
||||
|
||||
ffile:write(msg, "\n")
|
||||
|
||||
if options.keep_alive == nil or options.keep_alive ~= "true" then
|
||||
ffile:close()
|
||||
ffile = nil
|
||||
end
|
||||
end
|
||||
|
||||
function mod.file_cleanup()
|
||||
if ffile ~= nil then
|
||||
ffile:flush()
|
||||
ffile:close()
|
||||
ffile = nil
|
||||
end
|
||||
end
|
||||
|
||||
function mod.file_reopen(options)
|
||||
if options.keep_alive == "true" then
|
||||
mod.file_cleanup()
|
||||
mod.file_open(options)
|
||||
end
|
||||
end
|
||||
|
||||
function mod.syslog(event, rule, source, priority, priority_num, msg, format, hostname, options)
|
||||
mod.syslog_message(priority, priority_num, msg, options)
|
||||
end
|
||||
|
||||
function mod.syslog_message(priority, priority_num, msg, options)
|
||||
falco.syslog(priority_num, msg)
|
||||
end
|
||||
|
||||
function mod.syslog_cleanup()
|
||||
end
|
||||
|
||||
function mod.syslog_reopen()
|
||||
end
|
||||
|
||||
function mod.program_open(options)
|
||||
if pfile == nil then
|
||||
pfile = io.popen(options.program, "w")
|
||||
if options.buffered == 0 then
|
||||
pfile:setvbuf "no"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function mod.program(event, rule, source, priority, priority_num, msg, format, hostname, options)
|
||||
mod.program_message(priority, priority_num, msg, options)
|
||||
end
|
||||
|
||||
function mod.program_message(priority, priority_num, msg, options)
|
||||
-- XXX Ideally we'd check that the program ran
|
||||
-- successfully. However, the luajit we're using returns true even
|
||||
-- when the shell can't run the program.
|
||||
|
||||
-- Note: options are all strings
|
||||
if options.keep_alive == "true" then
|
||||
mod.program_open(options)
|
||||
else
|
||||
pfile = io.popen(options.program, "w")
|
||||
end
|
||||
|
||||
pfile:write(msg, "\n")
|
||||
|
||||
if options.keep_alive == nil or options.keep_alive ~= "true" then
|
||||
pfile:close()
|
||||
pfile = nil
|
||||
end
|
||||
end
|
||||
|
||||
function mod.program_cleanup()
|
||||
if pfile ~= nil then
|
||||
pfile:flush()
|
||||
pfile:close()
|
||||
pfile = nil
|
||||
end
|
||||
end
|
||||
|
||||
function mod.program_reopen(options)
|
||||
if options.keep_alive == "true" then
|
||||
mod.program_cleanup()
|
||||
mod.program_open(options)
|
||||
end
|
||||
end
|
||||
|
||||
function mod.http(event, rule, source, priority, priority_num, msg, format, hostname, options)
|
||||
mod.http_message(priority, priority_num, msg, options)
|
||||
end
|
||||
|
||||
function mod.http_message(priority, priority_num, msg, options)
|
||||
c_outputs.handle_http(options.url, msg)
|
||||
end
|
||||
|
||||
function mod.http_cleanup()
|
||||
end
|
||||
|
||||
function mod.http_reopen()
|
||||
end
|
||||
|
||||
function mod.grpc(event, rule, source, priority, priority_num, msg, format, hostname, options)
|
||||
fields = formats.resolve_tokens(event, source, format)
|
||||
c_outputs.handle_grpc(event, rule, source, priority, msg, fields, hostname, options)
|
||||
end
|
||||
|
||||
function mod.grpc_message(priority, priority_num, msg, options)
|
||||
-- todo(fntlnz, leodido) > gRPC does not support subscribing to dropped events yet
|
||||
end
|
||||
|
||||
|
||||
function mod.grpc_cleanup()
|
||||
end
|
||||
|
||||
function mod.grpc_reopen()
|
||||
end
|
||||
|
||||
function output_event(event, rule, source, priority, priority_num, format, hostname)
|
||||
-- If format starts with a *, remove it, as we're adding our own
|
||||
-- prefix here.
|
||||
if format:sub(1, 1) == "*" then
|
||||
format = format:sub(2)
|
||||
end
|
||||
|
||||
-- time_format_iso_8601 will be the same for all output channels
|
||||
time_format_iso_8601 = 0
|
||||
|
||||
for index, o in ipairs(outputs) do
|
||||
time_format_iso_8601 = o.options.time_format_iso_8601
|
||||
break
|
||||
end
|
||||
|
||||
if source == "syscall" then
|
||||
if time_format_iso_8601 == 1 then
|
||||
format = "*%evt.time.iso8601: " .. priority .. " " .. format
|
||||
else
|
||||
format = "*%evt.time: " .. priority .. " " .. format
|
||||
end
|
||||
else
|
||||
if time_format_iso_8601 == 1 then
|
||||
format = "*%jevt.time.iso8601: " .. priority .. " " .. format
|
||||
else
|
||||
format = "*%jevt.time: " .. priority .. " " .. format
|
||||
end
|
||||
end
|
||||
|
||||
msg = formats.format_event(event, rule, source, priority, format)
|
||||
|
||||
for index, o in ipairs(outputs) do
|
||||
o.output(event, rule, source, priority, priority_num, msg, format, hostname, o.options)
|
||||
end
|
||||
end
|
||||
|
||||
function output_msg(msg, priority, priority_num)
|
||||
for index, o in ipairs(outputs) do
|
||||
o.message(priority, priority_num, msg, o.options)
|
||||
end
|
||||
end
|
||||
|
||||
function output_cleanup()
|
||||
formats.free_formatters()
|
||||
for index, o in ipairs(outputs) do
|
||||
o.cleanup()
|
||||
end
|
||||
end
|
||||
|
||||
function output_reopen()
|
||||
for index, o in ipairs(outputs) do
|
||||
o.reopen(o.options)
|
||||
end
|
||||
end
|
||||
|
||||
function add_output(output_name, buffered, time_format_iso_8601, options)
|
||||
if not (type(mod[output_name]) == "function") then
|
||||
error("rule_loader.add_output(): invalid output_name: " .. output_name)
|
||||
end
|
||||
|
||||
-- outputs can optionally define a validation function so that we don't
|
||||
-- find out at runtime (when an event finally matches a rule!) that the options are invalid
|
||||
if (type(mod[output_name .. "_validate"]) == "function") then
|
||||
mod[output_name .. "_validate"](options)
|
||||
end
|
||||
|
||||
if options == nil then
|
||||
options = {}
|
||||
end
|
||||
|
||||
options.buffered = buffered
|
||||
options.time_format_iso_8601 = time_format_iso_8601
|
||||
|
||||
table.insert(
|
||||
outputs,
|
||||
{
|
||||
output = mod[output_name],
|
||||
cleanup = mod[output_name .. "_cleanup"],
|
||||
reopen = mod[output_name .. "_reopen"],
|
||||
message = mod[output_name .. "_message"],
|
||||
options = options
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
return mod
|
||||
@@ -1,44 +0,0 @@
|
||||
-- Copyright (C) 2019 The Falco Authors.
|
||||
--
|
||||
-- Licensed under the Apache License, Version 2.0 (the "License");
|
||||
-- you may not use this file except in compliance with the License.
|
||||
-- You may obtain a copy of the License at
|
||||
--
|
||||
-- http://www.apache.org/licenses/LICENSE-2.0
|
||||
--
|
||||
-- Unless required by applicable law or agreed to in writing, software
|
||||
-- distributed under the License is distributed on an "AS IS" BASIS,
|
||||
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
-- See the License for the specific language governing permissions and
|
||||
-- limitations under the License.
|
||||
--
|
||||
|
||||
local parser = require "parser"
|
||||
|
||||
if #arg ~= 1 then
|
||||
print("Usage: test.lua <string>")
|
||||
os.exit(1)
|
||||
end
|
||||
|
||||
local macros = {}
|
||||
local ast
|
||||
|
||||
local function doit(line)
|
||||
ast = parser.parse_filter(line)
|
||||
|
||||
if not ast then
|
||||
print("error", error_msg)
|
||||
os.exit(1)
|
||||
end
|
||||
|
||||
end
|
||||
for str in string.gmatch(arg[1], "([^;]+)") do
|
||||
doit(str)
|
||||
end
|
||||
|
||||
if (ast and ast.type) then
|
||||
parser.print_ast(ast)
|
||||
end
|
||||
|
||||
os.exit(0)
|
||||
|
||||
73
userspace/falco/outputs.h
Normal file
73
userspace/falco/outputs.h
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include "falco_common.h"
|
||||
#include "gen_filter.h"
|
||||
|
||||
namespace falco
|
||||
{
|
||||
namespace outputs
|
||||
{
|
||||
|
||||
//
|
||||
// The way to refer to an output (file, syslog, stdout, etc.)
|
||||
// An output has a name and set of options.
|
||||
//
|
||||
struct config
|
||||
{
|
||||
std::string name;
|
||||
std::map<std::string, std::string> options;
|
||||
};
|
||||
|
||||
//
|
||||
// This class acts as the primary interface for implementing
|
||||
// a Falco output class.
|
||||
//
|
||||
|
||||
class abstract_output
|
||||
{
|
||||
public:
|
||||
void init(config oc, bool buffered, std::string hostname)
|
||||
{
|
||||
m_oc = oc;
|
||||
m_buffered = buffered;
|
||||
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;
|
||||
|
||||
// Output a generic message. Not necessarily associated with any event.
|
||||
virtual void output_msg(falco_common::priority_type priority, std::string &msg) = 0;
|
||||
|
||||
virtual void reopen() {}
|
||||
|
||||
virtual void cleanup() {}
|
||||
|
||||
protected:
|
||||
config m_oc;
|
||||
bool m_buffered;
|
||||
std::string m_hostname;
|
||||
};
|
||||
|
||||
} // namespace outputs
|
||||
} // namespace falco
|
||||
63
userspace/falco/outputs_file.cpp
Normal file
63
userspace/falco/outputs_file.cpp
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
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 "outputs_file.h"
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include "banned.h" // This raises a compilation error when certain functions are used
|
||||
|
||||
void falco::outputs::output_file::open_file()
|
||||
{
|
||||
if(!m_buffered)
|
||||
{
|
||||
m_outfile.rdbuf()->pubsetbuf(0, 0);
|
||||
}
|
||||
if(!m_outfile.is_open())
|
||||
{
|
||||
m_outfile.open(m_oc.options["filename"], fstream::app);
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
open_file();
|
||||
m_outfile << msg + "\n";
|
||||
|
||||
if(m_oc.options["keep_alive"] != "true")
|
||||
{
|
||||
cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
void falco::outputs::output_file::cleanup()
|
||||
{
|
||||
if(m_outfile.is_open())
|
||||
{
|
||||
m_outfile.close();
|
||||
}
|
||||
}
|
||||
|
||||
void falco::outputs::output_file::reopen()
|
||||
{
|
||||
cleanup();
|
||||
open_file();
|
||||
}
|
||||
46
userspace/falco/outputs_file.h
Normal file
46
userspace/falco/outputs_file.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "outputs.h"
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
namespace falco
|
||||
{
|
||||
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 cleanup();
|
||||
|
||||
void reopen();
|
||||
|
||||
private:
|
||||
void open_file();
|
||||
|
||||
std::ofstream m_outfile;
|
||||
};
|
||||
|
||||
} // namespace outputs
|
||||
} // namespace falco
|
||||
76
userspace/falco/outputs_grpc.cpp
Normal file
76
userspace/falco/outputs_grpc.cpp
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
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 <google/protobuf/util/time_util.h>
|
||||
#include "outputs_grpc.h"
|
||||
#include "grpc_queue.h"
|
||||
#include "falco_common.h"
|
||||
#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)
|
||||
{
|
||||
falco::outputs::response grpc_res;
|
||||
|
||||
// time
|
||||
auto timestamp = grpc_res.mutable_time();
|
||||
*timestamp = google::protobuf::util::TimeUtil::NanosecondsToTimestamp(evt->get_ts());
|
||||
|
||||
// rule
|
||||
auto r = grpc_res.mutable_rule();
|
||||
*r = rule;
|
||||
|
||||
// source
|
||||
falco::schema::source s = falco::schema::source::SYSCALL;
|
||||
if(!falco::schema::source_Parse(source, &s))
|
||||
{
|
||||
throw falco_exception("Unknown source passed to output_grpc::output_event()");
|
||||
}
|
||||
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))
|
||||
{
|
||||
throw falco_exception("Unknown priority passed to output_grpc::output_event()");
|
||||
}
|
||||
grpc_res.set_priority(p);
|
||||
|
||||
// output
|
||||
auto output = grpc_res.mutable_output();
|
||||
*output = msg;
|
||||
|
||||
// output fields
|
||||
auto &fields = *grpc_res.mutable_output_fields();
|
||||
auto resolvedTkns = falco_formats::resolve_tokens(evt, source, format);
|
||||
for(const auto &kv : resolvedTkns)
|
||||
{
|
||||
fields[kv.first] = kv.second;
|
||||
}
|
||||
|
||||
// hostname
|
||||
auto host = grpc_res.mutable_hostname();
|
||||
*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
|
||||
}
|
||||
35
userspace/falco/outputs_grpc.h
Normal file
35
userspace/falco/outputs_grpc.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "outputs.h"
|
||||
|
||||
namespace falco
|
||||
{
|
||||
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);
|
||||
};
|
||||
|
||||
} // namespace outputs
|
||||
} // namespace falco
|
||||
54
userspace/falco/outputs_http.cpp
Normal file
54
userspace/falco/outputs_http.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
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 "outputs_http.h"
|
||||
#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)
|
||||
{
|
||||
CURL *curl = NULL;
|
||||
CURLcode res = CURLE_FAILED_INIT;
|
||||
struct curl_slist *slist1;
|
||||
slist1 = NULL;
|
||||
|
||||
curl = curl_easy_init();
|
||||
if(curl)
|
||||
{
|
||||
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_POSTFIELDSIZE, -1L);
|
||||
|
||||
res = curl_easy_perform(curl);
|
||||
|
||||
if(res != CURLE_OK)
|
||||
{
|
||||
falco_logger::log(LOG_ERR, "libcurl error: " + string(curl_easy_strerror(res)));
|
||||
}
|
||||
curl_easy_cleanup(curl);
|
||||
curl = NULL;
|
||||
curl_slist_free_all(slist1);
|
||||
slist1 = NULL;
|
||||
}
|
||||
}
|
||||
35
userspace/falco/outputs_http.h
Normal file
35
userspace/falco/outputs_http.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "outputs.h"
|
||||
|
||||
namespace falco
|
||||
{
|
||||
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);
|
||||
};
|
||||
|
||||
} // namespace outputs
|
||||
} // namespace falco
|
||||
65
userspace/falco/outputs_program.cpp
Normal file
65
userspace/falco/outputs_program.cpp
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
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 "outputs_program.h"
|
||||
#include <stdio.h>
|
||||
#include "banned.h" // This raises a compilation error when certain functions are used
|
||||
|
||||
void falco::outputs::output_program::open_pfile()
|
||||
{
|
||||
if(m_pfile == nullptr)
|
||||
{
|
||||
m_pfile = popen(m_oc.options["program"].c_str(), "w");
|
||||
|
||||
if(!m_buffered)
|
||||
{
|
||||
setvbuf(m_pfile, NULL, _IONBF, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
open_pfile();
|
||||
|
||||
fprintf(m_pfile, "%s\n", msg.c_str());
|
||||
|
||||
if(m_oc.options["keep_alive"] != "true")
|
||||
{
|
||||
cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
void falco::outputs::output_program::cleanup()
|
||||
{
|
||||
if(m_pfile != nullptr)
|
||||
{
|
||||
pclose(m_pfile);
|
||||
m_pfile = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void falco::outputs::output_program::reopen()
|
||||
{
|
||||
cleanup();
|
||||
open_pfile();
|
||||
}
|
||||
44
userspace/falco/outputs_program.h
Normal file
44
userspace/falco/outputs_program.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "outputs.h"
|
||||
|
||||
namespace falco
|
||||
{
|
||||
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 cleanup();
|
||||
|
||||
void reopen();
|
||||
|
||||
private:
|
||||
void open_pfile();
|
||||
|
||||
FILE *m_pfile;
|
||||
};
|
||||
|
||||
} // namespace outputs
|
||||
} // namespace falco
|
||||
45
userspace/falco/outputs_stdout.cpp
Normal file
45
userspace/falco/outputs_stdout.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
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 "outputs_stdout.h"
|
||||
#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)
|
||||
{
|
||||
//
|
||||
// By default, the stdout stream is fully buffered or line buffered
|
||||
// (if the stream can be determined to refer to an interactive device, e.g. in a TTY).
|
||||
// Just enable automatic flushing when unbuffered output is desired.
|
||||
// Note that it is set every time since other writings to the stdout can disable it.
|
||||
//
|
||||
if(!m_buffered)
|
||||
{
|
||||
std::cout << std::unitbuf;
|
||||
}
|
||||
std::cout << msg + "\n";
|
||||
}
|
||||
|
||||
void falco::outputs::output_stdout::cleanup()
|
||||
{
|
||||
std::cout.flush();
|
||||
}
|
||||
37
userspace/falco/outputs_stdout.h
Normal file
37
userspace/falco/outputs_stdout.h
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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "outputs.h"
|
||||
|
||||
namespace falco
|
||||
{
|
||||
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 cleanup();
|
||||
};
|
||||
|
||||
} // namespace outputs
|
||||
} // namespace falco
|
||||
31
userspace/falco/outputs_syslog.cpp
Normal file
31
userspace/falco/outputs_syslog.cpp
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.
|
||||
*/
|
||||
|
||||
#include "outputs_syslog.h"
|
||||
#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)
|
||||
{
|
||||
// Syslog output should not have any trailing newline
|
||||
::syslog(priority, "%s", msg.c_str());
|
||||
}
|
||||
35
userspace/falco/outputs_syslog.h
Normal file
35
userspace/falco/outputs_syslog.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "outputs.h"
|
||||
|
||||
namespace falco
|
||||
{
|
||||
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);
|
||||
};
|
||||
|
||||
} // namespace outputs
|
||||
} // namespace falco
|
||||
10
userspace/libhawk/hawk.h
Normal file
10
userspace/libhawk/hawk.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef HAWK_H
|
||||
#define HAWK_H
|
||||
extern void hawk_init();
|
||||
extern void hawk_destroy();
|
||||
|
||||
typedef void* hawk_engine;
|
||||
typedef void (*hawk_watch_rules_cb)(char* rules_content, hawk_engine* engine);
|
||||
extern void hawk_watch_rules(hawk_watch_rules_cb cb, hawk_engine* engine);
|
||||
|
||||
#endif //HAWK_H
|
||||
Reference in New Issue
Block a user