Compare commits

..

13 Commits

Author SHA1 Message Date
Mark Stemm
ff75db9477 Get all tests working
Now the context doesn't have a column pointer but it either a line for
yaml parse errors or an individual object (rule, macro, etc) from the
parsed yaml.
2019-07-05 15:35:56 -07:00
Mark Stemm
3b49a0a85f Get rid of error()s when compiling filters
Ensure that compiling filters for rules or macros doesn't result in
throwing lua errors. Instead, return a bool status + the return
value(s). If the status is false, the next return value is an error
message.
2019-07-05 15:35:56 -07:00
Mark Stemm
fc5858a3d6 Really fix expected output 2019-07-05 15:35:56 -07:00
Mark Stemm
23e28e32c2 Don't add newline to error
Instead, add it to the message printed containing the error.
2019-07-05 15:35:56 -07:00
Mark Stemm
2fef3f3dd5 Fix test expected output 2019-07-05 15:35:56 -07:00
Mark Stemm
4a0bb56586 Fix final error direct from load_rules
Next is tackling compile_filter.
2019-07-05 15:35:56 -07:00
Mark Stemm
b710217181 More invalid rules tests 2019-07-05 15:35:56 -07:00
Mark Stemm
01b3a0aa95 Only print a single context line
This matches outputs of other validators
2019-07-05 15:35:56 -07:00
Mark Stemm
eb7433f838 Add additional invalid rules tests
Most of load_rules is now converted.
2019-07-05 15:35:56 -07:00
Mark Stemm
ffc9ac56d4 New tests for error context
New tests for specific parse failures and expected output. What's
covered is:

 - Input not being parsable as yaml
 - Input not being yaml at all (lyaml handles this slightly
   differently).

In each case the return value and stdout output with validation are checked.
2019-07-05 15:35:56 -07:00
Mark Stemm
74e2833cd7 WIP on better error contexts
Change the semantics of lua load_rules to return a
successful/unsuccessful status instead of throwing errors when loading
fails. On success, load_rules returns [true, required_engine_version]
and on failure load_rules returns [false, row, col, error string]. The
row/col will be used to include context on error.

Falco's output itself now prints validation results to stdout, which
makes it easier to capture the output and pass it along. Log messages
continue to go to stderr.

Still need to finish going through load_rules and return all errors
directly instead of throwing a lua error.
2019-07-05 15:35:56 -07:00
Mark Stemm
d1a6666742 New flags to compare stdout/stderr, validate rules
New test options stdout_is/stderr_is do a direct comparison between
stdout/stderr and the provided value.

Test option validate_rules_file maps to -V arguments, which validate
rules and exits.
2019-07-05 15:35:56 -07:00
Mark Stemm
4830f6991c Add context to yaml parse errors
If the rules file can't be parsed as yaml, lyaml returns a line and
column number. Add some context showing the lines around the line number
and a pointer to the column.
2019-07-05 15:35:56 -07:00
59 changed files with 525 additions and 1387 deletions

View File

@@ -1,119 +0,0 @@
# --------------------------
# General Formatting Options
# --------------------------
# How wide to allow formatted cmake files
line_width = 80
# How many spaces to tab for indent
tab_size = 2
# If arglists are longer than this, break them always
max_subargs_per_line = 3
# If true, separate flow control names from their parentheses with a space
separate_ctrl_name_with_space = False
# If true, separate function names from parentheses with a space
separate_fn_name_with_space = False
# If a statement is wrapped to more than one line, than dangle the closing
# parenthesis on it's own line
dangle_parens = False
# If the statement spelling length (including space and parenthesis is larger
# than the tab width by more than this amoung, then force reject un-nested
# layouts.
max_prefix_chars = 2
# If a candidate layout is wrapped horizontally but it exceeds this many lines,
# then reject the layout.
max_lines_hwrap = 2
# What style line endings to use in the output.
line_ending = 'unix'
# Format command names consistently as 'lower' or 'upper' case
command_case = 'canonical'
# Format keywords consistently as 'lower' or 'upper' case
keyword_case = 'unchanged'
# Specify structure for custom cmake functions
additional_commands = {
"pkg_find": {
"kwargs": {
"PKG": "*"
}
}
}
# A list of command names which should always be wrapped
always_wrap = []
# Specify the order of wrapping algorithms during successive reflow attempts
algorithm_order = [0, 1, 2, 3, 4]
# If true, the argument lists which are known to be sortable will be sorted
# lexicographicall
enable_sort = True
# If true, the parsers may infer whether or not an argument list is sortable
# (without annotation).
autosort = False
# If a comment line starts with at least this many consecutive hash characters,
# then don't lstrip() them off. This allows for lazy hash rulers where the first
# hash char is not separated by space
hashruler_min_length = 10
# A dictionary containing any per-command configuration overrides. Currently
# only `command_case` is supported.
per_command = {}
# --------------------------
# Comment Formatting Options
# --------------------------
# What character to use for bulleted lists
bullet_char = '*'
# What character to use as punctuation after numerals in an enumerated list
enum_char = '.'
# enable comment markup parsing and reflow
enable_markup = True
# If comment markup is enabled, don't reflow the first comment block in each
# listfile. Use this to preserve formatting of your copyright/license
# statements.
first_comment_is_literal = False
# If comment markup is enabled, don't reflow any comment block which matches
# this (regex) pattern. Default is `None` (disabled).
literal_comment_pattern = None
# Regular expression to match preformat fences in comments
# default=r'^\s*([`~]{3}[`~]*)(.*)$'
fence_pattern = '^\\s*([`~]{3}[`~]*)(.*)$'
# Regular expression to match rulers in comments
# default=r'^\s*[^\w\s]{3}.*[^\w\s]{3}$'
ruler_pattern = '^\\s*[^\\w\\s]{3}.*[^\\w\\s]{3}$'
# If true, then insert a space between the first hash char and remaining hash
# chars in a hash ruler, and normalize it's length to fill the column
canonicalize_hashrulers = True
# ---------------------------------
# Miscellaneous Options
# ---------------------------------
# If true, emit the unicode byte-order mark (BOM) at the start of the file
emit_byteorder_mark = False
# Specify the encoding of the input file. Defaults to utf-8.
input_encoding = 'utf-8'
# Specify the encoding of the output file. Defaults to utf-8. Note that cmake
# only claims to support utf-8 so be careful when using anything else
output_encoding = 'utf-8'

5
.gitignore vendored
View File

@@ -20,7 +20,4 @@ docker/event-generator/mysqld
docker/event-generator/httpd
docker/event-generator/sha1sum
docker/event-generator/vipw
.vscode/*
.luacheckcache
.vscode/*

View File

@@ -1,9 +0,0 @@
std = "min"
cache = true
include_files = {
"userspace/falco/lua/*.lua",
"userspace/engine/lua/*.lua",
"userspace/engine/lua/lyaml/*.lua",
"*.luacheckrc"
}
exclude_files = {"build"}

View File

@@ -18,8 +18,8 @@
language: cpp
compiler: gcc
env:
- BUILD_TYPE=debug
- BUILD_TYPE=release
- BUILD_TYPE=Debug
- BUILD_TYPE=Release
sudo: required
services:
- docker
@@ -33,4 +33,15 @@ install:
- pushd ../sysdig && (git checkout "${BRANCH}" || exit 0) && echo "Using sysdig branch:" $(git rev-parse --abbrev-ref HEAD) && popd
script:
- mkdir build
- ./scripts/build "${TRAVIS_BUILD_DIR}/.." "${TRAVIS_BUILD_DIR}/build"
- cd build
- docker run --user $(id -u):$(id -g) -v /etc/passwd:/etc/passwd:ro -e MAKE_JOBS=4 -v $TRAVIS_BUILD_DIR/..:/source -v $TRAVIS_BUILD_DIR/build:/build falcosecurity/falco-builder cmake
- docker run --user $(id -u):$(id -g) -v /etc/passwd:/etc/passwd:ro -e MAKE_JOBS=4 -v $TRAVIS_BUILD_DIR/..:/source -v $TRAVIS_BUILD_DIR/build:/build falcosecurity/falco-builder package
- docker run --user $(id -u):$(id -g) -v /etc/passwd:/etc/passwd:ro -e MAKE_JOBS=1 -v $TRAVIS_BUILD_DIR/..:/source -v $TRAVIS_BUILD_DIR/build:/build falcosecurity/falco-builder tests
- docker run -v /boot:/boot:ro -v /var/run/docker.sock:/var/run/docker.sock -v /etc/passwd:/etc/passwd:ro -e MAKE_JOBS=4 -v $TRAVIS_BUILD_DIR/..:/source -v $TRAVIS_BUILD_DIR/build:/build falcosecurity/falco-tester
notifications:
webhooks:
urls:
# - https://webhooks.gitter.im/e/fdbc2356fb0ea2f15033
on_success: change
on_failure: always
on_start: never

View File

@@ -1,8 +0,0 @@
extends: default
rules:
indentation: disable
document-start: disable
comments: disable
line-length: disable
new-line-at-end-of-file: disable

View File

@@ -2,104 +2,6 @@
This file documents all notable changes to Falco. The release numbering uses [semantic versioning](http://semver.org).
## v0.17.0
Released 2019-07-31
## Major Changes
* **The set of supported platforms has changed**. Switch to a reorganized builder image that uses Centos 7 as a base. As a result, falco is no longer supported on Centos 6. The other supported platforms should remain the same [[#719](https://github.com/falcosecurity/falco/pull/719)]
## Minor Changes
* When enabling rules within the falco engine, use rule substrings instead of regexes. [[#743](https://github.com/falcosecurity/falco/pull/743)]
* Additional improvements to the handling and display of rules validation errors [[#744](https://github.com/falcosecurity/falco/pull/744)] [[#747](https://github.com/falcosecurity/falco/pull/747)]
## Bug Fixes
* Fix a problem that would cause prevent container metadata lookups when falco was daemonized [[#731](https://github.com/falcosecurity/falco/pull/731)]
* Allow rule priorites to be expressed as lowercase and a mix of lower/uppercase [[#737](https://github.com/falcosecurity/falco/pull/737)]
## Rule Changes
* Fix a parentheses bug with the `shell_procs` macro [[#728](https://github.com/falcosecurity/falco/pull/728)]
* Allow additional containers to mount sensitive host paths [[#733](https://github.com/falcosecurity/falco/pull/733)] [[#736](https://github.com/falcosecurity/falco/pull/736)]
* Allow additional containers to truncate log files [[#733](https://github.com/falcosecurity/falco/pull/733)]
* Fix false positives with the `Write below root` rule on GKE [[#739](https://github.com/falcosecurity/falco/pull/739)]
## v0.16.0
Released 2019-07-12
## Major Changes
* Clean up error reporting to provide more meaningful error messages along with context when loading rules files. When run with -V, the results of the validation ("OK" or error message) are sent to standard output. [[#708](https://github.com/falcosecurity/falco/pull/708)]
* Improve rule loading performance by optimizing lua parsing paths to avoid expensive pattern matches. [[#694](https://github.com/falcosecurity/falco/pull/694)]
* Bump falco engine version to 4 to reflect new fields `ka.useragent`, others. [[#710](https://github.com/falcosecurity/falco/pull/710)] [[#681](https://github.com/falcosecurity/falco/pull/681)]
* Add Catch2 as a unit testing framework. This will add additional coverage on top of the regression tests using Avocado. [[#687](https://github.com/falcosecurity/falco/pull/687)]
## Minor Changes
* Add SYSDIG_DIR Cmake option to specify location for sysdig source code when building falco. [[#677](https://github.com/falcosecurity/falco/pull/677)] [[#679](https://github.com/falcosecurity/falco/pull/679)] [[#702](https://github.com/falcosecurity/falco/pull/702)]
* New field `ka.useragent` reports the useragent from k8s audit events. [[#709](https://github.com/falcosecurity/falco/pull/709)]
* Add clang formatter for C++ syntax formatting. [[#701](https://github.com/falcosecurity/falco/pull/701)] [[#689](https://github.com/falcosecurity/falco/pull/689)]
* Partial changes towards lua syntax formatting. No particular formatting enforced yet, though. [[#718](https://github.com/falcosecurity/falco/pull/718)]
* Partial changes towards yaml syntax formatting. No particular formatting enforced yet, though. [[#714](https://github.com/falcosecurity/falco/pull/714)]
* Add cmake syntax formatting. [[#703](https://github.com/falcosecurity/falco/pull/703)]
* Token bucket unit tests and redesign. [[#692](https://github.com/falcosecurity/falco/pull/692)]
* Update github PR template. [[#699](https://github.com/falcosecurity/falco/pull/699)]
* Fix PR template for kind/rule-*. [[#697](https://github.com/falcosecurity/falco/pull/697)]
## Bug Fixes
* Remove an unused cmake file. [[#700](https://github.com/falcosecurity/falco/pull/700)]
* Misc Cmake cleanups. [[#673](https://github.com/falcosecurity/falco/pull/673)]
* Misc k8s install docs improvements. [[#671](https://github.com/falcosecurity/falco/pull/671)]
## Rule Changes
* Allow k8s.gcr.io/kube-proxy image to run privileged. [[#717](https://github.com/falcosecurity/falco/pull/717)]
* Add runc to the list of possible container entrypoint parents. [[#712](https://github.com/falcosecurity/falco/pull/712)]
* Skip Source RFC 1918 addresses when considering outbound connections. [[#685](https://github.com/falcosecurity/falco/pull/685)]
* Add additional `user_XXX` placeholder macros to allow for easy customization of rule exceptions. [[#685](https://github.com/falcosecurity/falco/pull/685)]
* Let weaveworks programs change namespaces. [[#685](https://github.com/falcosecurity/falco/pull/685)]
* Add additional openshift images. [[#685](https://github.com/falcosecurity/falco/pull/685)]
* Add openshift as a k8s binary. [[#678](https://github.com/falcosecurity/falco/pull/678)]
* Add dzdo as a binary that can change users. [[#678](https://github.com/falcosecurity/falco/pull/678)]
* Allow azure/calico binaries to change namespaces. [[#678](https://github.com/falcosecurity/falco/pull/678)]
* Add back trusted_containers list for backport compatibility [[#675](https://github.com/falcosecurity/falco/pull/675)]
* Add mkdirat as a syscall for mkdir operations. [[#667](https://github.com/falcosecurity/falco/pull/667)]
* Add container id/repository to rules that can work with containers. [[#667](https://github.com/falcosecurity/falco/pull/667)]
## v0.15.3
Released 2019-06-12

View File

@@ -15,40 +15,32 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
cmake_minimum_required(VERSION 3.3.2)
cmake_minimum_required(VERSION 2.8.2)
project(falco)
if(NOT SYSDIG_DIR)
get_filename_component(SYSDIG_DIR "${PROJECT_SOURCE_DIR}/../sysdig" REALPATH)
endif()
# Custom CMake modules
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules")
list(APPEND CMAKE_MODULE_PATH "${SYSDIG_DIR}/cmake/modules")
option(BUILD_WARNINGS_AS_ERRORS "Enable building with -Wextra -Werror flags")
if(NOT DEFINED FALCO_VERSION)
set(FALCO_VERSION "0.1.1dev")
endif()
if(NOT DEFINED FALCO_ETC_DIR)
set(FALCO_ETC_DIR "/etc/falco")
endif()
if(NOT CMAKE_BUILD_TYPE)
SET(CMAKE_BUILD_TYPE Release)
endif()
if(NOT DRAIOS_DEBUG_FLAGS)
set(DRAIOS_DEBUG_FLAGS "-D_DEBUG")
endif()
string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE)
if (CMAKE_BUILD_TYPE STREQUAL "debug")
set(KBUILD_FLAGS "${DRAIOS_DEBUG_FLAGS} ${DRAIOS_FEATURE_FLAGS}")
else()
set(CMAKE_BUILD_TYPE "release")
set(KBUILD_FLAGS "${DRAIOS_FEATURE_FLAGS}")
endif()
set(CMAKE_COMMON_FLAGS "-Wall -ggdb ${DRAIOS_FEATURE_FLAGS}")
if(BUILD_WARNINGS_AS_ERRORS)
set(CMAKE_SUPPRESSED_WARNINGS "-Wno-unused-parameter -Wno-unused-variable -Wno-unused-but-set-variable -Wno-missing-field-initializers -Wno-sign-compare -Wno-type-limits -Wno-implicit-fallthrough -Wno-format-truncation")
set(CMAKE_SUPPRESSED_WARNINGS "-Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-type-limits -Wno-implicit-fallthrough -Wno-format-truncation")
set(CMAKE_COMMON_FLAGS "${CMAKE_COMMON_FLAGS} -Wextra -Werror ${CMAKE_SUPPRESSED_WARNINGS}")
endif()
@@ -67,28 +59,11 @@ if(CMAKE_SYSTEM_NAME MATCHES "Linux")
add_definitions(-DHAS_CAPTURE)
endif()
# Create the falco version variable according to git index
if(NOT FALCO_VERSION)
include(GetGitRevisionDescription)
git_get_exact_tag(FALCO_TAG)
if(NOT FALCO_TAG)
git_describe(FALCO_VERSION "--always")
git_local_changes(FALCO_CHANGES)
if(FALCO_CHANGES STREQUAL "DIRTY")
string(TOLOWER "${FALCO_CHANGES}" FALCO_CHANGES)
set(FALCO_VERSION "${FALCO_VERSION}.${FALCO_CHANGES}")
endif()
set(FALCO_VERSION "0.${FALCO_VERSION}")
else()
set(FALCO_VERSION "${FALCO_TAG}")
string(REGEX
REPLACE "^v([0-9]+)(\\.[0-9]+)(\\.[0-9]+)?"
"\\1\\2\\3"
FALCO_VERSION
${FALCO_VERSION})
endif()
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
set(KBUILD_FLAGS "${DRAIOS_DEBUG_FLAGS} ${DRAIOS_FEATURE_FLAGS}")
else()
set(KBUILD_FLAGS "${DRAIOS_FEATURE_FLAGS}")
endif()
message(STATUS "Falco version: ${FALCO_VERSION}")
set(PACKAGE_NAME "falco")
set(PROBE_VERSION "${FALCO_VERSION}")
@@ -100,6 +75,10 @@ endif()
set(CMD_MAKE make)
if(NOT SYSDIG_DIR)
set(SYSDIG_DIR "${PROJECT_SOURCE_DIR}/../sysdig")
endif()
# make luaJIT work on OS X
if(APPLE)
set(CMAKE_EXE_LINKER_FLAGS "-pagezero_size 10000 -image_base 100000000")
@@ -161,7 +140,7 @@ else()
CONFIGURE_COMMAND ./configure --disable-maintainer-mode --enable-all-static --disable-dependency-tracking
BUILD_COMMAND ${CMD_MAKE} LDFLAGS=-all-static
BUILD_IN_SOURCE 1
PATCH_COMMAND curl -L https://github.com/stedolan/jq/commit/8eb1367ca44e772963e704a700ef72ae2e12babd.patch | patch
PATCH_COMMAND wget -O jq-1.5-fix-tokenadd.patch https://github.com/stedolan/jq/commit/8eb1367ca44e772963e704a700ef72ae2e12babd.patch && patch -i jq-1.5-fix-tokenadd.patch
INSTALL_COMMAND "")
endif()
@@ -615,7 +594,7 @@ else()
BUILD_BYPRODUCTS ${GRPC_LIB} ${GRPCPP_LIB}
# TODO s390x support
# TODO what if using system zlib
PATCH_COMMAND rm -rf third_party/zlib && ln -s ${ZLIB_SRC} third_party/zlib && curl -L https://download.sysdig.com/dependencies/grpc-1.1.4-Makefile.patch | patch
PATCH_COMMAND rm -rf third_party/zlib && ln -s ${ZLIB_SRC} third_party/zlib && wget https://download.sysdig.com/dependencies/grpc-1.1.4-Makefile.patch && patch < grpc-1.1.4-Makefile.patch
INSTALL_COMMAND "")
endif()
@@ -627,6 +606,14 @@ add_subdirectory(test)
add_subdirectory(rules)
add_subdirectory(docker)
# Add path for custom CMake modules used to build dependencies from Sysdig (libscap, libsinsp)
list(APPEND CMAKE_MODULE_PATH
"${SYSDIG_DIR}/cmake/modules")
# Add path for custom CMake modules
list(APPEND CMAKE_MODULE_PATH
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules")
if(CMAKE_SYSTEM_NAME MATCHES "Linux")
add_subdirectory("${SYSDIG_DIR}/driver" "${PROJECT_BINARY_DIR}/driver")
include(FindMakedev)
@@ -643,10 +630,10 @@ add_subdirectory(userspace/engine)
add_subdirectory(userspace/falco)
add_subdirectory(tests)
set(CPACK_PACKAGE_NAME "${PACKAGE_NAME}")
set(CPACK_PACKAGE_VENDOR "Sysdig Inc.")
set(CPACK_PACKAGE_CONTACT "opensource@sysdig.com")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Falco - Container Native Runtime Security")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "falco, a system-level activity monitoring tool")
set(CPACK_PACKAGE_DESCRIPTION_FILE "${PROJECT_SOURCE_DIR}/scripts/description.txt")
set(CPACK_PACKAGE_VERSION "${FALCO_VERSION}")
set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CMAKE_SYSTEM_PROCESSOR}")
@@ -656,19 +643,19 @@ set(CPACK_PACKAGE_RELOCATABLE "OFF")
set(CPACK_GENERATOR DEB RPM TGZ)
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Sysdig <support@sysdig.com>")
set(CPACK_DEBIAN_PACKAGE_SECTION "utils")
set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "amd64")
set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "https://www.falco.org")
set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "http://www.sysdig.org")
set(CPACK_DEBIAN_PACKAGE_DEPENDS "dkms (>= 2.1.0.0)")
set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CMAKE_BINARY_DIR}/scripts/debian/postinst;${CMAKE_BINARY_DIR}/scripts/debian/prerm;${PROJECT_SOURCE_DIR}/scripts/debian/postrm;${PROJECT_SOURCE_DIR}/cpack/debian/conffiles")
set(CPACK_RPM_PACKAGE_LICENSE "Apache v2.0")
set(CPACK_RPM_PACKAGE_URL "https://www.falco.org")
set(CPACK_RPM_PACKAGE_URL "http://www.sysdig.org")
set(CPACK_RPM_PACKAGE_REQUIRES "dkms, gcc, make, kernel-devel, perl")
set(CPACK_RPM_POST_INSTALL_SCRIPT_FILE "${PROJECT_SOURCE_DIR}/scripts/rpm/postinstall")
set(CPACK_RPM_PRE_UNINSTALL_SCRIPT_FILE "${PROJECT_SOURCE_DIR}/scripts/rpm/preuninstall")
set(CPACK_RPM_POST_UNINSTALL_SCRIPT_FILE "${PROJECT_SOURCE_DIR}/scripts/rpm/postuninstall")
set(CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION /usr/src /usr/share/man /usr/share/man/man8 /etc /usr /usr/bin /usr/share /etc/rc.d /etc/rc.d/init.d)
set(CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION /usr/src /usr/share/man /usr/share/man/man8 /etc /usr /usr/bin /usr/share /etc/rc.d /etc/rc.d/init.d )
set(CPACK_RPM_PACKAGE_RELOCATABLE "OFF")
include(CPack)

View File

@@ -5,7 +5,7 @@
#### Latest release
**v0.17.0**
**v0.15.3**
Read the [change log](https://github.com/falcosecurity/falco/blob/dev/CHANGELOG.md)
Dev Branch: [![Build Status](https://travis-ci.com/falcosecurity/falco.svg?branch=dev)](https://travis-ci.com/falcosecurity/falco)<br />

View File

@@ -1,168 +0,0 @@
# - Returns a version string from Git
#
# These functions force a re-configure on each git commit so that you can
# trust the values of the variables in your build system.
#
# get_git_head_revision(<refspecvar> <hashvar> [<additional arguments to git describe> ...])
#
# Returns the refspec and sha hash of the current head revision
#
# git_describe(<var> [<additional arguments to git describe> ...])
#
# Returns the results of git describe on the source tree, and adjusting
# the output so that it tests false if an error occurs.
#
# git_get_exact_tag(<var> [<additional arguments to git describe> ...])
#
# Returns the results of git describe --exact-match on the source tree,
# and adjusting the output so that it tests false if there was no exact
# matching tag.
#
# git_local_changes(<var>)
#
# Returns either "CLEAN" or "DIRTY" with respect to uncommitted changes.
# Uses the return code of "git diff-index --quiet HEAD --".
# Does not regard untracked files.
#
# Requires CMake 2.6 or newer (uses the 'function' command)
#
# Original Author:
# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
# http://academic.cleardefinition.com
# Iowa State University HCI Graduate Program/VRAC
#
# Copyright Iowa State University 2009-2010.
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
if(__get_git_revision_description)
return()
endif()
set(__get_git_revision_description YES)
# We must run the following at "include" time, not at function call time,
# to find the path to this module rather than the path to a calling list file
get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH)
function(get_git_head_revision _refspecvar _hashvar)
set(GIT_PARENT_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
set(GIT_DIR "${GIT_PARENT_DIR}/.git")
while(NOT EXISTS "${GIT_DIR}") # .git dir not found, search parent directories
set(GIT_PREVIOUS_PARENT "${GIT_PARENT_DIR}")
get_filename_component(GIT_PARENT_DIR ${GIT_PARENT_DIR} PATH)
if(GIT_PARENT_DIR STREQUAL GIT_PREVIOUS_PARENT)
# We have reached the root directory, we are not in git
set(${_refspecvar} "GITDIR-NOTFOUND" PARENT_SCOPE)
set(${_hashvar} "GITDIR-NOTFOUND" PARENT_SCOPE)
return()
endif()
set(GIT_DIR "${GIT_PARENT_DIR}/.git")
endwhile()
# check if this is a submodule
if(NOT IS_DIRECTORY ${GIT_DIR})
file(READ ${GIT_DIR} submodule)
string(REGEX REPLACE "gitdir: (.*)\n$" "\\1" GIT_DIR_RELATIVE ${submodule})
get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH)
get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} ABSOLUTE)
endif()
set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data")
if(NOT EXISTS "${GIT_DATA}")
file(MAKE_DIRECTORY "${GIT_DATA}")
endif()
if(NOT EXISTS "${GIT_DIR}/HEAD")
return()
endif()
set(HEAD_FILE "${GIT_DATA}/HEAD")
configure_file("${GIT_DIR}/HEAD" "${HEAD_FILE}" COPYONLY)
configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in"
"${GIT_DATA}/grabRef.cmake"
@ONLY)
include("${GIT_DATA}/grabRef.cmake")
set(${_refspecvar} "${HEAD_REF}" PARENT_SCOPE)
set(${_hashvar} "${HEAD_HASH}" PARENT_SCOPE)
endfunction()
function(git_describe _var)
if(NOT GIT_FOUND)
find_package(Git QUIET)
endif()
get_git_head_revision(refspec hash)
if(NOT GIT_FOUND)
set(${_var} "GIT-NOTFOUND" PARENT_SCOPE)
return()
endif()
if(NOT hash)
set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE)
return()
endif()
# TODO sanitize
#if((${ARGN}" MATCHES "&&") OR
# (ARGN MATCHES "||") OR
# (ARGN MATCHES "\\;"))
# message("Please report the following error to the project!")
# message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}")
#endif()
# message(STATUS "Arguments to execute_process: ${ARGN}")
execute_process(COMMAND
"${GIT_EXECUTABLE}"
describe
${hash}
${ARGN}
WORKING_DIRECTORY
"${CMAKE_CURRENT_SOURCE_DIR}"
RESULT_VARIABLE
res
OUTPUT_VARIABLE
out
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT res EQUAL 0)
set(out "${out}-${res}-NOTFOUND")
endif()
set(${_var} "${out}" PARENT_SCOPE)
endfunction()
function(git_get_exact_tag _var)
git_describe(out --exact-match ${ARGN})
set(${_var} "${out}" PARENT_SCOPE)
endfunction()
function(git_local_changes _var)
if(NOT GIT_FOUND)
find_package(Git QUIET)
endif()
get_git_head_revision(refspec hash)
if(NOT GIT_FOUND)
set(${_var} "GIT-NOTFOUND" PARENT_SCOPE)
return()
endif()
if(NOT hash)
set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE)
return()
endif()
execute_process(COMMAND
"${GIT_EXECUTABLE}"
diff-index --quiet HEAD --
WORKING_DIRECTORY
"${CMAKE_CURRENT_SOURCE_DIR}"
RESULT_VARIABLE
res
OUTPUT_VARIABLE
out
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE)
if(res EQUAL 0)
set(${_var} "CLEAN" PARENT_SCOPE)
else()
set(${_var} "DIRTY" PARENT_SCOPE)
endif()
endfunction()

View File

@@ -1,41 +0,0 @@
#
# Internal file for GetGitRevisionDescription.cmake
#
# Requires CMake 2.6 or newer (uses the 'function' command)
#
# Original Author:
# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
# http://academic.cleardefinition.com
# Iowa State University HCI Graduate Program/VRAC
#
# Copyright Iowa State University 2009-2010.
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
set(HEAD_HASH)
file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024)
string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS)
if(HEAD_CONTENTS MATCHES "ref")
# named branch
string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}")
if(EXISTS "@GIT_DIR@/${HEAD_REF}")
configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY)
else()
configure_file("@GIT_DIR@/packed-refs" "@GIT_DATA@/packed-refs" COPYONLY)
file(READ "@GIT_DATA@/packed-refs" PACKED_REFS)
if(${PACKED_REFS} MATCHES "([0-9a-z]*) ${HEAD_REF}")
set(HEAD_HASH "${CMAKE_MATCH_1}")
endif()
endif()
else()
# detached HEAD
configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY)
endif()
if(NOT HEAD_HASH)
file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024)
string(STRIP "${HEAD_HASH}" HEAD_HASH)
endif()

View File

@@ -1,49 +1,52 @@
FROM centos:7
FROM centos:6
LABEL name="falcosecurity/falco-builder"
LABEL usage="docker run -v $PWD/..:/source -v $PWD/build:/build falcosecurity/falco-builder cmake"
LABEL maintainer="opensource@sysdig.com"
ENV FALCO_VERSION 0.1.1dev
ENV BUILD_TYPE Release
ENV BUILD_DRIVER OFF
ENV BUILD_BPF OFF
ENV BUILD_WARNINGS_AS_ERRORS ON
ENV MAKE_JOBS 4
ARG BUILD_TYPE=release
ARG BUILD_DRIVER=OFF
ARG BUILD_BPF=OFF
ARG BUILD_WARNINGS_AS_ERRORS=ON
ARG MAKE_JOBS=4
ARG FALCO_VERSION
# copied from builder script
RUN curl -o /etc/yum.repos.d/devtools-2.repo https://people.centos.org/tru/devtools-2/devtools-2.repo && \
rpm -i http://mirror.pnl.gov/epel/6/i386/epel-release-6-8.noarch.rpm && \
sed -e 's,$basearch,i386,' -e 's,$releasever\],$releasever-i686\],' /etc/yum.repos.d/devtools-2.repo > /etc/yum.repos.d/devtools-2-i686.repo && \
yum -y install \
createrepo \
devtoolset-2-toolchain \
dpkg \
dpkg-devel \
expect \
gcc \
gcc-c++ \
git \
glibc-static \
libcurl-devel \
make \
curl \
libcurl-devel \
zlib-devel \
pkg-config \
rpm-build \
unzip \
wget \
tar \
autoconf \
automake \
libtool && \
yum -y install \
glibc-devel.i686 \
devtoolset-2-libstdc++-devel.i686 \
devtoolset-2-elfutils-libelf-devel && \
yum clean all
RUN curl -o docker.tgz https://get.docker.com/builds/Linux/x86_64/docker-1.11.0.tgz && \
tar xfz docker.tgz docker/docker && \
mv docker/docker /usr/local/bin/docker && \
chmod +x /usr/local/bin/docker && \
rm -fr docker.tgz docker/
ENV BUILD_TYPE=${BUILD_TYPE}
ENV BUILD_DRIVER=${BUILD_DRIVER}
ENV BUILD_BPF=${BUILD_BPF}
ENV BUILD_WARNINGS_AS_ERRORS=${BUILD_WARNINGS_AS_ERRORS}
ENV MAKE_JOBS=${MAKE_JOBS}
ENV FALCO_VERSION=${FALCO_VERSION}
# TEMPORARY until dependencies in CMakeLists.txt are fixed
RUN yum -y install libyaml-devel
COPY entrypoint.sh /
ARG DOCKER_VERSION=1.11.0
ARG CMAKE_VERSION=3.5.0
RUN yum -y install centos-release-scl && \
INSTALL_PKGS="devtoolset-7-gcc devtoolset-7-gcc-c++ devtoolset-7-toolchain devtoolset-7-libstdc++-devel devtoolset-7-elfutils-libelf-devel glibc-static autoconf automake libtool createrepo expect git which libcurl-devel zlib-devel rpm-build" && \
yum -y install --setopt=tsflags=nodocs $INSTALL_PKGS && \
rpm -V $INSTALL_PKGS
RUN source scl_source enable devtoolset-7 && \
cd /tmp && \
curl -L https://github.com/kitware/cmake/releases/download/v${CMAKE_VERSION}/cmake-${CMAKE_VERSION}.tar.gz | tar xz; \
cd cmake-${CMAKE_VERSION} && \
./bootstrap --system-curl && \
make -j${MAKE_JOBS} && \
make install && \
rm -rf /tmp/cmake-${CMAKE_VERSION}
# fixme: deps needs a fix into CMakeLists.txt
RUN yum -y install libyaml-devel && yum clean all -y
COPY ./root /
# DTS
ENV BASH_ENV=/usr/bin/scl_enable \
ENV=/usr/bin/scl_enable \
PROMPT_COMMAND=". /usr/bin/scl_enable"
ENTRYPOINT ["entrypoint"]
CMD ["usage"]
ENTRYPOINT ["/entrypoint.sh"]

40
docker/builder/entrypoint.sh Executable file
View File

@@ -0,0 +1,40 @@
#!/bin/bash
set -euxo pipefail
SOURCE_DIR=/source
BUILD_DIR=/build
TASK=${1:-all}
MANPATH=
. /opt/rh/devtoolset-2/enable
# Download and install cmake if not downloaded
CMAKE_DIR=$BUILD_DIR/cmake
if [ ! -e $CMAKE_DIR ]; then
cd $BUILD_DIR
mkdir -p $BUILD_DIR/cmake
wget -nv https://s3.amazonaws.com/download.draios.com/dependencies/cmake-3.3.2.tar.gz
tar -C $CMAKE_DIR --strip-components 1 -xzf cmake-3.3.2.tar.gz
cd $CMAKE_DIR
./bootstrap --system-curl
make -j$MAKE_JOBS
fi
if [ $TASK == "cmake" ]; then
mkdir -p $BUILD_DIR/$BUILD_TYPE
cd $BUILD_DIR/$BUILD_TYPE
$CMAKE_DIR/bin/cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DFALCO_VERSION=$FALCO_VERSION -DCMAKE_INSTALL_PREFIX=/usr -DBUILD_DRIVER=${BUILD_DRIVER} -DBUILD_BPF=${BUILD_BPF} -DBUILD_WARNINGS_AS_ERRORS=${BUILD_WARNINGS_AS_ERRORS} $SOURCE_DIR/falco
exit 0
fi
if [ $TASK == "bash" ]; then
exec /bin/bash
fi
cd $BUILD_DIR/$BUILD_TYPE
make -j$MAKE_JOBS $TASK

View File

@@ -1,62 +0,0 @@
#!/usr/bin/env bash
set -eu -o pipefail
SOURCE_DIR=/source
BUILD_DIR=/build
CMD=${1:-usage}
shift
# Build type can be "debug" or "release", fallbacks to "release" by default
BUILD_TYPE=$(echo "$BUILD_TYPE" | tr "[:upper:]" "[:lower:]")
DRAIOS_DEBUG_FLAGS=
case "$BUILD_TYPE" in
"debug")
DRAIOS_DEBUG_FLAGS="-D_DEBUG -DNDEBUG"
;;
*)
BUILD_TYPE="release"
;;
esac
case "$CMD" in
"cmake")
# Check that source directory contains Falco and Sysdig
if [ ! -d "$SOURCE_DIR/sysdig" ]; then
echo "Missing sysdig source." >&2
exit 1
fi
if [ ! -d "$SOURCE_DIR/falco" ]; then
echo "Missing falco source." >&2
exit 1
fi
# Prepare build directory
mkdir -p "$BUILD_DIR/$BUILD_TYPE"
cd "$BUILD_DIR/$BUILD_TYPE"
cmake \
-DCMAKE_BUILD_TYPE="$BUILD_TYPE" \
-DCMAKE_INSTALL_PREFIX=/usr \
-DBUILD_DRIVER="$BUILD_DRIVER" \
-DBUILD_BPF="$BUILD_BPF" \
-DBUILD_WARNINGS_AS_ERRORS="$BUILD_WARNINGS_AS_ERRORS" \
-DFALCO_VERSION="$FALCO_VERSION" \
-DDRAIOS_DEBUG_FLAGS="$DRAIOS_DEBUG_FLAGS" \
"$SOURCE_DIR/falco"
exit "$(printf '%d\n' $?)"
;;
"bash")
CMD=/bin/bash
;& # fallthrough
"usage")
exec "$CMD" "$@"
;;
*)
if [ ! -d "$BUILD_DIR/$BUILD_TYPE" ]; then
echo "Missing $BUILD_DIR/$BUILD_TYPE directory: run cmake."
exit 1
fi
cd "$BUILD_DIR/$BUILD_TYPE"
make -j"$MAKE_JOBS" "$CMD"
;;
esac

View File

@@ -1,6 +0,0 @@
# IMPORTANT: Do not add more content to this file unless you know what you are doing.
# This file is sourced everytime the shell session is opened.
#
# This will make scl collection binaries work out of box.
unset BASH_ENV PROMPT_COMMAND ENV
source scl_source enable devtoolset-7

View File

@@ -1,51 +0,0 @@
#!/usr/bin/env bash
gccversion=$(gcc --version | head -n1)
cppversion=$(g++ -dM -E -x c++ /dev/null | grep -F __cplusplus | cut -d' ' -f3)
cmakeversion=$(cmake --version | head -n1)
cat <<EOF
Hello, this is the Falco builder container.
How to use.
The default commands for the Falco builder image reports usage and environment info.
* docker run falcosecurity/falco-builder
* docker run falcosecurity/falco-builder usage
It supports bash.
* docker run -ti falcosecurity/falco-builder bash
To build Falco it needs:
- a bind-mount on the source directory (ie., the directory containing falco and sysdig source as siblings)
Optionally, you can also bind-mount the build directory.
So, you can execute it from the Falco root directory as follows.
* docker run -v $PWD/..:/source -v $PWD/build:/build falcosecurity/falco-builder cmake
* docker run -v $PWD/..:/source -v $PWD/build:/build falcosecurity/falco-builder [<cmake-target-x>, ..., <cmake-target-y>]
Eg.,
* docker run -v $PWD/..:/source -v $PWD/build:/build falcosecurity/falco-builder tests
* docker run -v $PWD/..:/source -v $PWD/build:/build falcosecurity/falco-builder install
How to build.
* cd docker/builder && DOCKER_BUILDKIT=1 docker build -t falcosecurity/falco-builder .
In case you want to customise the builder at build time the following build arguments are provided:
- BUILD_TYPE whether you want a "release" or "debug" build (defaults to "release").
- BUILD_DRIVER whether to build the driver or not (defaults to "OFF")
- BUILD_BPF whether to build the BPF driver or not (defaults to "OFF")
- BUILD_WARNINGS_AS_ERRORS whether to intend warnings as errors or not (defaults to "ON")
- MAKE_JOBS the number of jobs to use during make (defaults to "4")
- FALCO_VERSION the version to label the build (built from git index in case it is missing)
It is possible to change these at runtime (in the container) since environment variables with the same names are provided, too.
Environment.
* ${gccversion}
* cplusplus ${cppversion}
* ${cmakeversion}
EOF

View File

@@ -1,12 +1,12 @@
FROM debian:unstable
LABEL usage="docker run -i -t -v /var/run/docker.sock:/host/var/run/docker.sock -v /dev:/host/dev -v /proc:/host/proc:ro -v /boot:/host/boot:ro -v /lib/modules:/host/lib/modules:ro -v /usr:/host/usr:ro --name NAME IMAGE"
LABEL maintainer="opensource@sysdig.com"
LABEL maintainer="Sysdig <support@sysdig.com>"
ARG FALCO_VERSION=
RUN test -n FALCO_VERSION
ARG FALCO_VERSION=0.1.1dev
ENV FALCO_VERSION ${FALCO_VERSION}
LABEL RUN="docker run -i -t -v /var/run/docker.sock:/host/var/run/docker.sock -v /dev:/host/dev -v /proc:/host/proc:ro -v /boot:/host/boot:ro -v /lib/modules:/host/lib/modules:ro -v /usr:/host/usr:ro --name NAME IMAGE"
ENV SYSDIG_HOST_ROOT /host
ENV HOME /root

View File

@@ -1,16 +1,17 @@
FROM fedora:28
FROM centos:7
LABEL name="falcosecurity/falco-tester"
LABEL usage="docker run -v /boot:/boot:ro -v /var/run/docker.sock:/var/run/docker.sock -v $PWD/..:/source -v $PWD/build:/build -e FALCO_VERSION=<current_falco_version> --name <name> falcosecurity/falco-tester test"
LABEL maintainer="opensource@sysdig.com"
ENV FALCO_VERSION 0.1.1dev
ENV BUILD_TYPE Release
ENV FALCO_VERSION=
ENV BUILD_TYPE=release
RUN yum -y install epel-release && \
yum -y install \
python-pip \
docker \
jq \
unzip
RUN curl https://avocado-project.org/data/repos/avocado-fedora.repo -o /etc/yum.repos.d/avocado.repo && \
dnf install -y docker findutils jq unzip python2-avocado python2-avocado-plugins-varianter-yaml-to-mux && dnf clean all
RUN pip install avocado-framework avocado-framework-plugin-varianter-yaml-to-mux
COPY ./root /
COPY entrypoint.sh /
ENTRYPOINT ["entrypoint"]
CMD ["usage"]
ENTRYPOINT ["/entrypoint.sh"]

24
docker/tester/entrypoint.sh Executable file
View File

@@ -0,0 +1,24 @@
#!/bin/bash
set -euxo pipefail
SOURCE_DIR=/source
BUILD_DIR=/build
TASK=${1:-test}
if [ $TASK == "test" ]; then
echo "Building local docker image falcosecurity/falco:test from latest debian package..."
cp $BUILD_DIR/$BUILD_TYPE/falco*.deb $BUILD_DIR/$BUILD_TYPE/docker/local
cd $BUILD_DIR/$BUILD_TYPE/docker/local && docker build --build-arg FALCO_VERSION=${FALCO_VERSION} -t falcosecurity/falco:test .
echo "Running regression tests"
cd $SOURCE_DIR/falco/test
bash run_regression_tests.sh $BUILD_DIR/$BUILD_TYPE
docker rmi falcosecurity/falco:test || true
exit 0
fi
if [ $TASK == "bash" ]; then
exec /bin/bash
fi

View File

@@ -1,57 +0,0 @@
#!/usr/bin/env bash
set -eu -o pipefail
SOURCE_DIR=/source
BUILD_DIR=/build
CMD=${1:-test}
shift
# Build type can be "debug" or "release", fallbacks to "release" by default
BUILD_TYPE=$(echo "$BUILD_TYPE" | tr "[:upper:]" "[:lower:]")
case "$BUILD_TYPE" in
"debug")
;;
*)
BUILD_TYPE="release"
;;
esac
case "$CMD" in
"test")
if [ ! -d "$BUILD_DIR/$BUILD_TYPE/docker/local" ]; then
echo "Missing $BUILD_DIR/$BUILD_TYPE/docker/local directory." >&2
exit 1
fi
if [ -z "$FALCO_VERSION" ]; then
echo "Missing Falco version." >&2
exit 1
fi
PACKAGE="$BUILD_DIR/$BUILD_TYPE/falco-$FALCO_VERSION-x86_64.deb"
if [ ! -f "$PACKAGE" ]; then
echo "Package(s) not found." >&2
exit 1
fi
DOCKER_IMAGE_NAME="falcosecurity/falco:test"
echo "Building local docker image $DOCKER_IMAGE_NAME from latest debian package..."
cp "$PACKAGE" $BUILD_DIR/$BUILD_TYPE/docker/local
cd $BUILD_DIR/$BUILD_TYPE/docker/local
docker build --build-arg FALCO_VERSION="$FALCO_VERSION" -t "$DOCKER_IMAGE_NAME" .
# Check that source directory contains Falco and Sysdig
if [ ! -d "$SOURCE_DIR/falco/test" ]; then
echo "Missing $SOURCE_DIR/falco/test directory." >&2
exit 1
fi
echo "Running regression tests ..."
cd $SOURCE_DIR/falco/test
bash run_regression_tests.sh $BUILD_DIR/$BUILD_TYPE
docker rmi "$DOCKER_IMAGE_NAME" || true
;;
"bash")
CMD=/bin/bash
;& # fallthrough
"usage")
exec "$CMD" "$@"
;;
esac

View File

@@ -1,41 +0,0 @@
#!/usr/bin/env bash
pythonversion=$(python -c 'import sys; version=sys.version_info[:3]; print("{0}.{1}.{2}".format(*version))')
pipversion=$(pip --version | cut -d' ' -f 1,2,5,6)
dockerversion=$(docker --version)
avocadoversion=$(pip show avocado-framework | grep Version)
avocadoversion=${avocadoversion#"Version: "}
cat <<EOF
Hello, this is the Falco tester container.
How to use.
The default commands for the Falco tester image reports usage and environment info.
* docker run falcosecurity/falco-tester
* docker run falcosecurity/falco-tester usage
It supports bash.
* docker run -ti falcosecurity/falco-tester bash
To run Falco regression tests you need to provide:
- the docker socket
- the boot directory
- the source directory
- the directory where Falco has been built
- the environment variable FALCO_VARIABLE set to the value obtained during the Falco's build
Assuming you are running it from the Falco root directory, you can run it as follows.
* docker run -v /boot:/boot:ro -v /var/run/docker.sock:/var/run/docker.sock -v $PWD/..:/source -v $PWD/build:/build -e FALCO_VERSION=<current_falco_version> falcosecurity/falco-tester test
How to build.
* cd docker/builder && DOCKER_BUILDKIT=1 docker build -t falcosecurity/falco-tester .
Environment.
* python ${pythonversion}
* ${pipversion}
* avocado ${avocadoversion}
* ${dockerversion}
EOF

View File

@@ -111,7 +111,7 @@
items: [add-shell, remove-shell]
- macro: shell_procs
condition: proc.name in (shell_binaries)
condition: (proc.name in (shell_binaries))
- list: coreutils_binaries
items: [
@@ -1265,7 +1265,7 @@
- list: known_root_files
items: [/root/.monit.state, /root/.auth_tokens, /root/.bash_history, /root/.ash_history, /root/.aws/credentials,
/root/.viminfo.tmp, /root/.lesshst, /root/.bzr.log, /root/.gitconfig.lock, /root/.babel.json, /root/.localstack,
/root/.node_repl_history, /root/.mongorc.js, /root/.dbshell, /root/.augeas/history, /root/.rnd, /root/.wget-hsts, /health, /exec.fifo]
/root/.node_repl_history, /root/.mongorc.js, /root/.dbshell, /root/.augeas/history, /root/.rnd, /root/.wget-hsts, /health]
- list: known_root_directories
items: [/root/.oracle_jre_usage, /root/.ssh, /root/.subversion, /root/.nami]
@@ -1740,7 +1740,7 @@
docker.io/sysdig/agent, docker.io/sysdig/falco, docker.io/sysdig/sysdig,
gcr.io/google_containers/kube-proxy, docker.io/calico/node,
docker.io/rook/toolbox, docker.io/cloudnativelabs/kube-router, docker.io/mesosphere/mesos-slave,
docker.io/docker/ucp-agent, sematext_images, k8s.gcr.io/kube-proxy
docker.io/docker/ucp-agent, sematext_images
]
- macro: falco_privileged_containers
@@ -1774,8 +1774,7 @@
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,
docker.io/datadog/docker-dd-agent, docker.io/datadog/agent, docker.io/docker/ucp-agent, docker.io/gliderlabs/logspout,
docker.io/netdata/netdata, docker.io/google/cadvisor, docker.io/prom/node-exporter
docker.io/datadog/docker-dd-agent, docker.io/datadog/agent, docker.io/docker/ucp-agent, docker.io/gliderlabs/logspout
]
- macro: falco_sensitive_mount_containers
@@ -1833,7 +1832,7 @@
# when we lose events and lose track of state.
- macro: container_entrypoint
condition: (not proc.pname exists or proc.pname in (runc:[0:PARENT], runc:[1:CHILD], runc, docker-runc, exe))
condition: (not proc.pname exists or proc.pname in (runc:[0:PARENT], runc:[1:CHILD], docker-runc, exe))
- rule: Launch Sensitive Mount Container
desc: >
@@ -2254,7 +2253,7 @@
condition: >
spawned_process and container and
((proc.name = "nc" and (proc.args contains "-e" or proc.args contains "-c")) or
(proc.name = "ncat" and (proc.args contains "--sh-exec" or proc.args contains "--exec" or proc.args contains "-e "
(proc.name = "ncat" and (proc.args contains "--sh-exec" or proc.args contains "--exec" or proc.args contains "-e "
or proc.args contains "-c " or proc.args contains "--lua-exec"))
)
output: >
@@ -2342,16 +2341,12 @@
- macro: allowed_clear_log_files
condition: (never_true)
- macro: trusted_logging_images
condition: (container.image.repository endswith "splunk/fluentd-hec")
- rule: Clear Log Activities
desc: Detect clearing of critical log files
condition: >
open_write and
access_log_files and
evt.arg.flags contains "O_TRUNC" and
not trusted_logging_images and
not allowed_clear_log_files
output: >
Log files were tampered (user=%user.name command=%proc.cmdline file=%fd.name container_id=%container.id image=%container.image.repository)

View File

@@ -1,26 +0,0 @@
#!/usr/bin/env bash
set -xeuo pipefail
SOURCE_DIR=$1
BUILD_DIR=$2
FALCOBUILDER_IMAGE="falcosecurity/falco-builder"
FALCOTESTER_IMAGE="falcosecurity/falco-tester"
docker run --user "$(id -u)":"$(id -g)" -v /etc/passwd:/etc/passwd:ro -e BUILD_TYPE="$BUILD_TYPE" -v "$SOURCE_DIR":/source -v "$BUILD_DIR":/build "$FALCOBUILDER_IMAGE" cmake
docker run --user "$(id -u)":"$(id -g)" -v /etc/passwd:/etc/passwd:ro -e BUILD_TYPE="$BUILD_TYPE" -v "$SOURCE_DIR":/source -v "$BUILD_DIR":/build "$FALCOBUILDER_IMAGE" package
docker run --user "$(id -u)":"$(id -g)" -v /etc/passwd:/etc/passwd:ro -e BUILD_TYPE="$BUILD_TYPE" -v "$SOURCE_DIR":/source -v "$BUILD_DIR":/build "$FALCOBUILDER_IMAGE" tests
# Deduct currently built version
CURRENT_FALCO_VERSION=$(docker run -v "$BUILD_DIR":/build "$FALCOBUILDER_IMAGE" bash -c "./build/$BUILD_TYPE/userspace/falco/falco --version" | cut -d' ' -f3 | tr -d '\r')
# Execute regression tests
docker run \
-v /boot:/boot:ro \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /etc/passwd:/etc/passwd:ro \
-v "$SOURCE_DIR":/source \
-v "$BUILD_DIR":/build \
-e BUILD_TYPE="$BUILD_TYPE" \
-e FALCO_VERSION="$CURRENT_FALCO_VERSION" \
"$FALCOTESTER_IMAGE" test

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env bash
#!/bin/bash
#
# Copyright (C) 2016-2018 Draios Inc dba Sysdig.
#
@@ -21,17 +21,17 @@ set -ex
PREFIX=$1
if [ -z "$PREFIX" ]; then
if [ -z $PREFIX ]; then
PREFIX=.
fi
mkdir -p $PREFIX
gcc -O2 -fPIC -I"$LUA_INCLUDE" -c lpcap.c -o $PREFIX/lpcap.o
gcc -O2 -fPIC -I"$LUA_INCLUDE" -c lpcode.c -o $PREFIX/lpcode.o
gcc -O2 -fPIC -I"$LUA_INCLUDE" -c lpprint.c -o $PREFIX/lpprint.o
gcc -O2 -fPIC -I"$LUA_INCLUDE" -c lptree.c -o $PREFIX/lptree.o
gcc -O2 -fPIC -I"$LUA_INCLUDE" -c lpvm.c -o $PREFIX/lpvm.o
gcc -O2 -fPIC -I$LUA_INCLUDE -c lpcap.c -o $PREFIX/lpcap.o
gcc -O2 -fPIC -I$LUA_INCLUDE -c lpcode.c -o $PREFIX/lpcode.o
gcc -O2 -fPIC -I$LUA_INCLUDE -c lpprint.c -o $PREFIX/lpprint.o
gcc -O2 -fPIC -I$LUA_INCLUDE -c lptree.c -o $PREFIX/lptree.o
gcc -O2 -fPIC -I$LUA_INCLUDE -c lpvm.c -o $PREFIX/lpvm.o
# For building lpeg.so, which we don't need now that we're statically linking lpeg.a into falco

View File

@@ -1 +1,3 @@
Falco is an open source project for intrusion and abnormality detection for Cloud Native platforms such as Kubernetes, Mesosphere, and Cloud Foundry. Detect abnormal application behavior. Alert via Slack, Fluentd, NATS, and more. Protect your platform by taking action through serverless (FaaS) frameworks, or other automation.
Sysdig Falco instruments your physical and virtual machines at the OS level by installing into the Linux kernel and capturing system calls and other OS events.
Then, using a rule-based configuration, you can specify filters for events of interest that you would like to log or be notified of.

View File

@@ -1,4 +1,3 @@
#!/usr/bin/env bash
#
# Copyright (C) 2016-2018 Draios Inc dba Sysdig.
#
@@ -17,6 +16,8 @@
# limitations under the License.
#
#!/bin/bash
cat ../sysdig/userspace/libscap/syscall_info_table.c | grep EF_DROP_FALCO | sed -e 's/.*\"\(.*\)\".*/\1/' | sort > ignored_syscall_info_table.txt
cat ../sysdig/driver/event_table.c | grep EF_DROP_FALCO | sed -e 's/[^\"]*\"\([^\"]*\)\".*/\1/' | sort | uniq > ignored_driver_event_table.txt
cat ../sysdig/userspace/libscap/event_table.c | grep EF_DROP_FALCO | sed -e 's/[^\"]*\"\([^\"]*\)\".*/\1/' | sort | uniq > ignored_userspace_event_table.txt

View File

@@ -1,95 +1,66 @@
void setBuildStatus(String context, String message, String state) {
step([
$class: "GitHubCommitStatusSetter",
reposSource: [
$class: "ManuallyEnteredRepositorySource",
url: "https://github.com/falcosecurity/falco"
],
contextSource: [
$class: "ManuallyEnteredCommitContextSource",
context: context
],
errorHandlers: [[
$class: "ChangingBuildStatusErrorHandler",
result: "UNSTABLE"
]],
statusResultSource: [
$class: "ConditionalStatusResultSource",
results: [[
$class: "AnyBuildResult",
message: message,
state: state
]]
]
]);
$class: "GitHubCommitStatusSetter",
reposSource: [$class: "ManuallyEnteredRepositorySource", url: "https://github.com/falcosecurity/falco"],
contextSource: [$class: "ManuallyEnteredCommitContextSource", context: context],
errorHandlers: [[$class: "ChangingBuildStatusErrorHandler", result: "UNSTABLE"]],
statusResultSource: [ $class: "ConditionalStatusResultSource", results: [[$class: "AnyBuildResult", message: message, state: state]] ]
]);
}
def version = 'UNKNOWN'
pipeline {
agent { label "agent-docker-builder" }
stages {
stage("Check out dependencies") {
steps {
dir("falco") {
checkout([
$class: "GitSCM",
branches: [[name: "refs/heads/"+env.BRANCH_NAME]],
doGenerateSubmoduleConfigurations: false,
extensions: [],
submoduleCfg: [],
userRemoteConfigs: [[
credentialsId: "github-jenkins-user-token",
url: "https://github.com/falcosecurity/falco"
]]
])
}
dir("sysdig") {
checkout([
$class: "GitSCM",
branches: [[name: "dev"]],
doGenerateSubmoduleConfigurations: false,
extensions: [],
submoduleCfg: [],
userRemoteConfigs: [[
credentialsId: "github-jenkins-user-token",
url: "https://github.com/draios/sysdig"
]]
])
steps {
dir("falco") {
checkout([$class: "GitSCM",
branches: [[name: "refs/heads/"+env.BRANCH_NAME]],
doGenerateSubmoduleConfigurations: false,
extensions: [],
submoduleCfg: [],
userRemoteConfigs: [[credentialsId: "github-jenkins-user-token", url: "https://github.com/draios/falco"]]])
}
dir("sysdig") {
checkout([$class: "GitSCM",
branches: [[name: "dev"]],
doGenerateSubmoduleConfigurations: false,
extensions: [],
submoduleCfg: [],
userRemoteConfigs: [[credentialsId: "github-jenkins-user-token", url: "https://github.com/draios/sysdig"]]])
}
}
}
stage("Build") {
steps {
script{
version = sh(returnStdout: true, script: "./falco/scripts/jenkins/build-pipeline/version")
sh("./falco/scripts/jenkins/build-pipeline/build ${version}")
}
}
post {
success {
setBuildStatus("Build", "Build Successful", "SUCCESS")
}
failure {
setBuildStatus("Build", "Build Failed", "FAILURE")
}
}
}
stage("Build") {
steps {
script{
sh("./falco/scripts/jenkins/build-pipeline/build.sh")
}
}
post {
success {
setBuildStatus("Build", "Build Successful", "SUCCESS")
}
failure {
setBuildStatus("Build", "Build Failed", "FAILURE")
}
}
}
stage("Run tests") {
steps {
script{
sh("./falco/scripts/jenkins/build-pipeline/run-tests ${version}")
}
}
post {
success {
setBuildStatus("Run tests", "All tests passed", "SUCCESS")
}
failure {
setBuildStatus("Run tests", "One or more tests failed", "FAILURE")
}
}
steps {
script{
sh("./falco/scripts/jenkins/build-pipeline/run-tests.sh")
}
}
post {
success {
setBuildStatus("Run tests", "All tests passed", "SUCCESS")
}
failure {
setBuildStatus("Run tests", "One or more tests failed", "FAILURE")
}
}
}
}
post {
always {

View File

@@ -1,28 +0,0 @@
#!/usr/bin/env bash
set -eu -o pipefail
if [ $# -eq 0 ]; then
>&2 echo "Missing arguments."
exit 1
fi
if [ -z "$1" ]; then
>&2 echo "Missing version."
exit 1
fi
if [ "$1" == "UNKNOWN" ]; then
>&2 echo "Unknown version."
exit 1
fi
FALCO_VERSION=$1
BUILD_DIR="${WORKSPACE}/build"
FALCOBUILDER_IMAGE="falcosecurity/falco-builder"
docker pull $FALCOBUILDER_IMAGE
rm -rf "$BUILD_DIR"
mkdir "$BUILD_DIR"
docker run -u "$(id -u):$(id -g)" -v /etc/passwd:/etc/passwd:ro -e FALCO_VERSION="$FALCO_VERSION" -v "$WORKSPACE":/source -v "$BUILD_DIR":/build "$FALCOBUILDER_IMAGE" cmake
docker run -u "$(id -u):$(id -g)" -v /etc/passwd:/etc/passwd:ro -e FALCO_VERSION="$FALCO_VERSION" -v $"$WORKSPACE":/source -v "$BUILD_DIR":/build "$FALCOBUILDER_IMAGE" package

View File

@@ -0,0 +1,11 @@
#!/bin/bash
set -xeuo pipefail
export FALCO_VERSION=0.1.$((2700+BUILD_NUMBER))dev
rm -rf ${WORKSPACE}/build
mkdir ${WORKSPACE}/build
docker run --user $(id -u):$(id -g) -v /etc/passwd:/etc/passwd:ro -e FALCO_VERSION=${FALCO_VERSION} -e MAKE_JOBS=4 -v ${WORKSPACE}:/source -v ${WORKSPACE}/build:/build falcosecurity/falco-builder cmake
docker run --user $(id -u):$(id -g) -v /etc/passwd:/etc/passwd:ro -e FALCO_VERSION=${FALCO_VERSION} -e MAKE_JOBS=4 -v ${WORKSPACE}:/source -v ${WORKSPACE}/build:/build falcosecurity/falco-builder package

View File

@@ -1,33 +0,0 @@
#!/usr/bin/env bash
set -eu -o pipefail
if [ $# -eq 0 ]; then
>&2 echo "Missing arguments."
exit 1
fi
if [ -z "$1" ]; then
>&2 echo "Missing version."
exit 1
fi
if [ "$1" == "UNKNOWN" ]; then
>&2 echo "Unknown version."
exit 1
fi
FALCO_VERSION=$1
BUILD_DIR="${WORKSPACE}/build"
FALCOTESTER_IMAGE="falcosecurity/falco-tester"
docker pull $FALCOTESTER_IMAGE
docker run \
-v /boot:/boot:ro \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /etc/passwd:/etc/passwd:ro \
-e FALCO_VERSION="$FALCO_VERSION" \
-v "$WORKSPACE":/source \
-v "$BUILD_DIR":/build \
"$FALCOTESTER_IMAGE" test
exit 0

View File

@@ -0,0 +1,10 @@
#!/bin/bash
set -xeuo pipefail
export FALCO_VERSION=0.1.$((2700+BUILD_NUMBER))dev
docker pull falcosecurity/falco-tester
docker run -v /boot:/boot:ro -v /var/run/docker.sock:/var/run/docker.sock -v /etc/passwd:/etc/passwd:ro -e FALCO_VERSION=${FALCO_VERSION} -v ${WORKSPACE}:/source -v ${WORKSPACE}/build:/build falcosecurity/falco-tester
exit 0

View File

@@ -1,23 +0,0 @@
#!/usr/bin/env bash
# Do not add "x"
set -eu -o pipefail
BUILD_DIR="${WORKSPACE}/build"
FALCOBUILDER_IMAGE="falcosecurity/falco-builder"
docker pull $FALCOBUILDER_IMAGE > /dev/null
rm -rf "$BUILD_DIR"
mkdir "$BUILD_DIR"
CMAKE_CMD="docker run -u $(id -u):$(id -g) -v /etc/passwd:/etc/passwd:ro -v $WORKSPACE:/source -v $BUILD_DIR:/build -a stdout -a stderr $FALCOBUILDER_IMAGE cmake"
FALCO_VERSION=$($CMAKE_CMD | grep -oP "Falco version: v?\K(\d+)\.[a-z0-9]{1,7}(\.[a-z0-9]+)?" || echo "UNKNOWN")
if [ "$FALCO_VERSION" == "UNKNOWN" ]; then
>&2 echo "Unknown version."
exit 1
fi
echo "$FALCO_VERSION.$((2700+BUILD_NUMBER))"

1
test/.gitignore vendored
View File

@@ -1 +0,0 @@
falco_traces.yaml

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env bash
#!/bin/bash
#
# Copyright (C) 2016-2018 Draios Inc dba Sysdig.
#

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python
#
# Copyright (C) 2016-2018 Draios Inc dba Sysdig.
#
@@ -34,10 +34,9 @@ class FalcoTest(Test):
"""
Load the sysdig kernel module if not already loaded.
"""
build_type = "release"
build_type = "Release"
if 'BUILD_TYPE' in os.environ:
build_type = os.environ['BUILD_TYPE'].lower()
build_type = "debug" if build_type == "debug" else "release"
build_type = os.environ['BUILD_TYPE']
build_dir = os.path.join('/build', build_type)
self.falcodir = self.params.get('falcodir', '/', default=os.path.join(self.basedir, build_dir))
@@ -269,7 +268,7 @@ class FalcoTest(Test):
triggered_rules = match.group(1)
for rule, count in self.detect_counts.iteritems():
expected = '\s{}: (\d+)'.format(re.sub(r'([$\.*+?()[\]{}|^])', r'\\\1', rule))
expected = '\s{}: (\d+)'.format(rule)
match = re.search(expected, triggered_rules)
if match is None:

View File

@@ -86,15 +86,6 @@ trace_files: !mux
- rules/rule_names_with_spaces.yaml
trace_file: trace_files/cat_write.scap
rule_names_with_regex_chars:
detect: True
detect_level: WARNING
rules_file:
- rules/rule_names_with_regex_chars.yaml
detect_counts:
- 'Open From Cat ($\.*+?()[]{}|^)': 8
trace_file: trace_files/cat_write.scap
multiple_rules_first_empty:
detect: True
detect_level: WARNING
@@ -402,81 +393,6 @@ trace_files: !mux
- rules/rule_append_failure.yaml
trace_file: trace_files/cat_write.scap
invalid_overwrite_macro:
exit_status: 1
stdout_contains: |+
.*invalid_base_macro.yaml: Ok
.*invalid_overwrite_macro.yaml: Compilation error when compiling "foo": Undefined macro 'foo' used in filter.
---
- macro: some macro
condition: foo
append: false
---
validate_rules_file:
- rules/invalid_base_macro.yaml
- rules/invalid_overwrite_macro.yaml
trace_file: trace_files/cat_write.scap
invalid_append_macro:
exit_status: 1
stdout_contains: |+
.*invalid_base_macro.yaml: Ok
.*invalid_append_macro.yaml: Compilation error when compiling "evt.type=execve foo": 17: syntax error, unexpected 'foo', expecting 'or', 'and'
---
- macro: some macro
condition: evt.type=execve
- macro: some macro
condition: foo
append: true
---
validate_rules_file:
- rules/invalid_base_macro.yaml
- rules/invalid_append_macro.yaml
trace_file: trace_files/cat_write.scap
invalid_overwrite_rule:
exit_status: 1
stdout_contains: |+
.*invalid_base_rule.yaml: Ok
.*invalid_overwrite_rule.yaml: Undefined macro 'bar' used in filter.
---
- rule: some rule
desc: some desc
condition: bar
output: some output
priority: INFO
append: false
---
validate_rules_file:
- rules/invalid_base_rule.yaml
- rules/invalid_overwrite_rule.yaml
trace_file: trace_files/cat_write.scap
invalid_append_rule:
exit_status: 1
stdout_contains: |+
.*invalid_base_rule.yaml: Ok
.*invalid_append_rule.yaml: Compilation error when compiling "evt.type=open bar": 15: syntax error, unexpected 'bar', expecting 'or', 'and'
---
- rule: some rule
desc: some desc
condition: evt.type=open
output: some output
priority: INFO
- rule: some rule
desc: some desc
condition: bar
output: some output
priority: INFO
append: true
---
validate_rules_file:
- rules/invalid_base_rule.yaml
- rules/invalid_append_rule.yaml
trace_file: trace_files/cat_write.scap
invalid_missing_rule_name:
exit_status: 1
stdout_is: |+
@@ -531,13 +447,13 @@ trace_files: !mux
- open_from_cat
trace_file: trace_files/cat_write.scap
disabled_rules_using_substring:
disabled_rules_using_regex:
detect: False
rules_file:
- rules/empty_rules.yaml
- rules/single_rule.yaml
disabled_rules:
- "open_from"
- "open.*"
trace_file: trace_files/cat_write.scap
disabled_rules_using_enabled_flag:

View File

@@ -1,3 +0,0 @@
- macro: some macro
condition: foo
append: true

View File

@@ -1,6 +0,0 @@
- rule: some rule
desc: some desc
condition: bar
output: some output
priority: INFO
append: true

View File

@@ -1,2 +0,0 @@
- macro: some macro
condition: evt.type=execve

View File

@@ -1,5 +0,0 @@
- rule: some rule
desc: some desc
condition: evt.type=open
output: some output
priority: INFO

View File

@@ -1,5 +0,0 @@
- rule: condition not rule
condition:
desc: some desc
output: some output
priority: INFO

View File

@@ -1,2 +0,0 @@
- macro: macro with comp error
condition: gak

View File

@@ -1,3 +0,0 @@
- macro: some macro
condition: foo
append: false

View File

@@ -1,6 +0,0 @@
- rule: some rule
desc: some desc
condition: bar
output: some output
priority: INFO
append: false

View File

@@ -1,25 +0,0 @@
#
# Copyright (C) 2016-2018 Draios Inc dba Sysdig.
#
# This file is part of falco.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
- macro: is_cat
condition: proc.name=cat
- rule: Open From Cat ($\.*+?()[]{}|^)
desc: A process named cat does an open
condition: evt.type=open and is_cat
output: "An open was seen (command=%proc.cmdline)"
priority: WARNING

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env bash
#!/bin/bash
#
# Copyright (C) 2016-2018 Draios Inc dba Sysdig.
#

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env bash
#!/bin/bash
#
# Copyright (C) 2016-2018 Draios Inc dba Sysdig.
#

Binary file not shown.

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env bash
#!/bin/bash
#
# Copyright (C) 2016-2018 Draios Inc dba Sysdig.
#
@@ -23,4 +23,4 @@
# The remaining arguments are taken from the command line.
exec sudo sysdig not evt.type in '(mprotect,brk,mq_timedreceive,mq_receive,mq_timedsend,mq_send,getrusage,procinfo,rt_sigprocmask,rt_sigaction,ioctl,clock_getres,clock_gettime,clock_nanosleep,clock_settime,close,epoll_create,epoll_create1,epoll_ctl,epoll_pwait,epoll_wait,eventfd,fcntl,fcntl64,fstat,fstat64,fstatat64,fstatfs,fstatfs64,futex,getitimer,gettimeofday,ioprio_get,ioprio_set,llseek,lseek,lstat,lstat64,mmap,mmap2,munmap,nanosleep,poll,ppoll,pread,pread64,preadv,procinfo,pselect6,pwrite,pwrite64,pwritev,read,readv,recv,recvfrom,recvmmsg,recvmsg,sched_yield,select,send,sendfile,sendfile64,sendmmsg,sendmsg,sendto,setitimer,settimeofday,shutdown,splice,stat,stat64,statfs,statfs64,switch,tee,timer_create,timer_delete,timerfd_create,timerfd_gettime,timerfd_settime,timer_getoverrun,timer_gettime,timer_settime,wait4,write,writev) and user.name!=ec2-user' "$@"
exec sudo sysdig not evt.type in '(mprotect,brk,mq_timedreceive,mq_receive,mq_timedsend,mq_send,getrusage,procinfo,rt_sigprocmask,rt_sigaction,ioctl,clock_getres,clock_gettime,clock_nanosleep,clock_settime,close,epoll_create,epoll_create1,epoll_ctl,epoll_pwait,epoll_wait,eventfd,fcntl,fcntl64,fstat,fstat64,fstatat64,fstatfs,fstatfs64,futex,getitimer,gettimeofday,ioprio_get,ioprio_set,llseek,lseek,lstat,lstat64,mmap,mmap2,munmap,nanosleep,poll,ppoll,pread,pread64,preadv,procinfo,pselect6,pwrite,pwrite64,pwritev,read,readv,recv,recvfrom,recvmmsg,recvmsg,sched_yield,select,send,sendfile,sendfile64,sendmmsg,sendmsg,sendto,setitimer,settimeofday,shutdown,splice,stat,stat64,statfs,statfs64,switch,tee,timer_create,timer_delete,timerfd_create,timerfd_gettime,timerfd_settime,timer_getoverrun,timer_gettime,timer_settime,wait4,write,writev) and user.name!=ec2-user' $@

View File

@@ -206,17 +206,17 @@ void falco_engine::load_rules_file(const string &rules_filename, bool verbose, b
load_rules(rules_content, verbose, all_events, required_engine_version);
}
void falco_engine::enable_rule(const string &substring, bool enabled, const string &ruleset)
void falco_engine::enable_rule(const string &pattern, bool enabled, const string &ruleset)
{
uint16_t ruleset_id = find_ruleset_id(ruleset);
m_sinsp_rules->enable(substring, enabled, ruleset_id);
m_k8s_audit_rules->enable(substring, enabled, ruleset_id);
m_sinsp_rules->enable(pattern, enabled, ruleset_id);
m_k8s_audit_rules->enable(pattern, enabled, ruleset_id);
}
void falco_engine::enable_rule(const string &substring, bool enabled)
void falco_engine::enable_rule(const string &pattern, bool enabled)
{
enable_rule(substring, enabled, m_default_ruleset);
enable_rule(pattern, enabled, m_default_ruleset);
}
void falco_engine::enable_rule_by_tag(const set<string> &tags, bool enabled, const string &ruleset)

View File

@@ -76,17 +76,16 @@ public:
void load_rules(const std::string &rules_content, bool verbose, bool all_events, uint64_t &required_engine_version);
//
// Enable/Disable any rules matching the provided substring.
// If the substring is "", all rules are enabled/disabled.
// When provided, enable/disable these rules in the
// Enable/Disable any rules matching the provided pattern
// (regex). When provided, enable/disable these rules in the
// context of the provided ruleset. The ruleset (id) can later
// be passed as an argument to process_event(). This allows
// for different sets of rules being active at once.
//
void enable_rule(const std::string &substring, bool enabled, const std::string &ruleset);
void enable_rule(const std::string &pattern, bool enabled, const std::string &ruleset);
// Wrapper that assumes the default ruleset
void enable_rule(const std::string &substring, bool enabled);
void enable_rule(const std::string &pattern, bool enabled);
//
// Enable/Disable any rules with any of the provided tags (set, exact matches only)

View File

@@ -19,9 +19,9 @@ limitations under the License.
// The version of rules/filter fields/etc supported by this falco
// engine.
#define FALCO_ENGINE_VERSION (4)
#define FALCO_ENGINE_VERSION (3)
// This is the result of running "falco --list -N | sha256sum" and
// represents the fields supported by this version of falco. It's used
// at build time to detect a changed set of fields.
#define FALCO_FIELDS_CHECKSUM "ceb069d9f9b2d4ebcc5de39bddc53b7af2e6b8f072edc293668fd6ac4e532413"
#define FALCO_FIELDS_CHECKSUM "9b5557ec8f16f5606a1544573b152d211d5212f653ee039146836a17266ff449"

View File

@@ -19,8 +19,8 @@ limitations under the License.
#include <ctype.h>
#include "uri.h"
#include "utils.h"
#include "uri.h"
#include "falco_common.h"
#include "json_evt.h"
@@ -30,6 +30,7 @@ using namespace std;
json_event::json_event()
{
}
json_event::~json_event()
@@ -59,7 +60,7 @@ std::string json_event_filter_check::def_format(const json &j, std::string &fiel
std::string json_event_filter_check::json_as_string(const json &j)
{
if(j.type() == json::value_t::string)
if (j.type() == json::value_t::string)
{
return j;
}
@@ -69,35 +70,32 @@ std::string json_event_filter_check::json_as_string(const json &j)
}
}
json_event_filter_check::field_info::field_info():
m_idx_mode(IDX_NONE), m_idx_type(IDX_NUMERIC)
json_event_filter_check::field_info::field_info()
: m_idx_mode(IDX_NONE), m_idx_type(IDX_NUMERIC)
{
}
json_event_filter_check::field_info::field_info(std::string name,
std::string desc):
m_name(name),
m_desc(desc),
m_idx_mode(IDX_NONE), m_idx_type(IDX_NUMERIC)
std::string desc)
: m_name(name), m_desc(desc),
m_idx_mode(IDX_NONE), m_idx_type(IDX_NUMERIC)
{
}
json_event_filter_check::field_info::field_info(std::string name,
std::string desc,
index_mode mode):
m_name(name),
m_desc(desc),
m_idx_mode(mode), m_idx_type(IDX_NUMERIC)
index_mode mode)
: m_name(name), m_desc(desc),
m_idx_mode(mode), m_idx_type(IDX_NUMERIC)
{
}
json_event_filter_check::field_info::field_info(std::string name,
std::string desc,
index_mode mode,
index_type itype):
m_name(name),
m_desc(desc),
m_idx_mode(mode), m_idx_type(itype)
index_type itype)
: m_name(name), m_desc(desc),
m_idx_mode(mode), m_idx_type(itype)
{
}
@@ -109,15 +107,14 @@ json_event_filter_check::alias::alias()
{
}
json_event_filter_check::alias::alias(nlohmann::json::json_pointer ptr):
m_jptr(ptr), m_format(def_format)
json_event_filter_check::alias::alias(nlohmann::json::json_pointer ptr)
: m_jptr(ptr), m_format(def_format)
{
}
json_event_filter_check::alias::alias(nlohmann::json::json_pointer ptr,
format_t format):
m_jptr(ptr),
m_format(format)
format_t format)
: m_jptr(ptr), m_format(format)
{
}
@@ -125,8 +122,8 @@ json_event_filter_check::alias::~alias()
{
}
json_event_filter_check::json_event_filter_check():
m_format(def_format)
json_event_filter_check::json_event_filter_check()
: m_format(def_format)
{
}
@@ -153,7 +150,7 @@ int32_t json_event_filter_check::parse_field_name(const char *str, bool alloc_st
// What follows the match must not be alphanumeric or a dot
if(strncmp(info.m_name.c_str(), str, info.m_name.size()) == 0 &&
!isalnum((int)str[info.m_name.size()]) &&
!isalnum((int) str[info.m_name.size()]) &&
str[info.m_name.size()] != '.' &&
info.m_name.size() > match_len)
{
@@ -172,7 +169,7 @@ int32_t json_event_filter_check::parse_field_name(const char *str, bool alloc_st
if(end != NULL)
{
m_idx = string(start, end - start);
m_idx = string(start, end-start);
}
idx_len = (end - start + 2);
@@ -200,14 +197,14 @@ int32_t json_event_filter_check::parse_field_name(const char *str, bool alloc_st
return match_len + idx_len;
}
void json_event_filter_check::add_filter_value(const char *str, uint32_t len, uint32_t i)
void json_event_filter_check::add_filter_value(const char* str, uint32_t len, uint32_t i)
{
m_values.push_back(string(str));
}
bool json_event_filter_check::compare(gen_event *evt)
{
json_event *jevt = (json_event *)evt;
json_event *jevt = (json_event *) evt;
std::string value = extract(jevt);
@@ -228,7 +225,7 @@ bool json_event_filter_check::compare(gen_event *evt)
case CO_IN:
for(auto &val : m_values)
{
if(value == val)
if (value == val)
{
return true;
}
@@ -271,12 +268,11 @@ json_event_filter_check::check_info &json_event_filter_check::get_fields()
return m_info;
}
uint8_t *json_event_filter_check::extract(gen_event *evt, uint32_t *len, bool sanitize_strings)
uint8_t* json_event_filter_check::extract(gen_event *evt, uint32_t* len, bool sanitize_strings)
{
json_event *jevt = (json_event *)evt;
json_event *jevt = (json_event *) evt;
try
{
try {
const json &j = jevt->jevt().at(m_jptr);
// Only format when the value was actually found in
@@ -290,7 +286,7 @@ uint8_t *json_event_filter_check::extract(gen_event *evt, uint32_t *len, bool sa
*len = m_tstr.size();
return (uint8_t *)m_tstr.c_str();
return (uint8_t *) m_tstr.c_str();
}
std::string json_event_filter_check::extract(json_event *evt)
@@ -303,7 +299,7 @@ std::string json_event_filter_check::extract(json_event *evt)
if(res != NULL)
{
ret.assign((const char *)res, len);
ret.assign((const char *) res, len);
}
return ret;
@@ -319,15 +315,18 @@ jevt_filter_check::jevt_filter_check()
{
m_info = {"jevt",
"generic ways to access json events",
{{s_jevt_time_field, "json event timestamp as a string that includes the nanosecond part"},
{s_jevt_time_iso_8601_field, "json event timestamp in ISO 8601 format, including nanoseconds and time zone offset (in UTC)"},
{s_jevt_rawtime_field, "absolute event timestamp, i.e. nanoseconds from epoch."},
{s_jevt_value_field, "General way to access single property from json object. The syntax is [<json pointer expression>]. The property is returned as a string", IDX_REQUIRED, IDX_KEY},
{s_jevt_obj_field, "The entire json object, stringified"}}};
{
{s_jevt_time_field, "json event timestamp as a string that includes the nanosecond part"},
{s_jevt_time_iso_8601_field, "json event timestamp in ISO 8601 format, including nanoseconds and time zone offset (in UTC)"},
{s_jevt_rawtime_field, "absolute event timestamp, i.e. nanoseconds from epoch."},
{s_jevt_value_field, "General way to access single property from json object. The syntax is [<json pointer expression>]. The property is returned as a string", IDX_REQUIRED, IDX_KEY},
{s_jevt_obj_field, "The entire json object, stringified"}
}};
}
jevt_filter_check::~jevt_filter_check()
{
}
int32_t jevt_filter_check::parse_field_name(const char *str, bool alloc_state, bool needed_for_filtering)
@@ -361,56 +360,55 @@ int32_t jevt_filter_check::parse_field_name(const char *str, bool alloc_state, b
const char *end;
// What follows must be [<json pointer expression>]
if(*(str + s_jevt_value_field.size()) != '[' ||
((end = strchr(str + 1, ']')) == NULL))
if (*(str + s_jevt_value_field.size()) != '[' ||
((end = strchr(str + 1, ']')) == NULL))
{
throw falco_exception(string("Could not parse filtercheck field \"") + str + "\". Did not have expected format with 'jevt.value[<json pointer>]'");
}
try
{
m_jptr = json::json_pointer(string(str + (s_jevt_value_field.size() + 1), (end - str - (s_jevt_value_field.size() + 1))));
try {
m_jptr = json::json_pointer(string(str + (s_jevt_value_field.size()+1), (end-str-(s_jevt_value_field.size()+1))));
}
catch(json::parse_error &e)
catch (json::parse_error& e)
{
throw falco_exception(string("Could not parse filtercheck field \"") + str + "\". Invalid json selector (" + e.what() + ")");
}
// The +1 accounts for the closing ']'
m_field = string(str, end - str + 1);
m_field = string(str, end-str + 1);
return (end - str + 1);
}
return 0;
}
uint8_t *jevt_filter_check::extract(gen_event *evt, uint32_t *len, bool sanitize_stings)
uint8_t* jevt_filter_check::extract(gen_event *evt, uint32_t* len, bool sanitize_stings)
{
if(m_field == s_jevt_rawtime_field)
{
m_tstr = to_string(evt->get_ts());
*len = m_tstr.size();
return (uint8_t *)m_tstr.c_str();
return (uint8_t *) m_tstr.c_str();
}
else if(m_field == s_jevt_time_field)
{
sinsp_utils::ts_to_string(evt->get_ts(), &m_tstr, false, true);
*len = m_tstr.size();
return (uint8_t *)m_tstr.c_str();
return (uint8_t *) m_tstr.c_str();
}
else if(m_field == s_jevt_time_iso_8601_field)
{
sinsp_utils::ts_to_iso_8601(evt->get_ts(), &m_tstr);
*len = m_tstr.size();
return (uint8_t *)m_tstr.c_str();
return (uint8_t *) m_tstr.c_str();
}
else if(m_field == s_jevt_obj_field)
{
json_event *jevt = (json_event *)evt;
json_event *jevt = (json_event *) evt;
m_tstr = jevt->jevt().dump();
*len = m_tstr.size();
return (uint8_t *)m_tstr.c_str();
return (uint8_t *) m_tstr.c_str();
}
return json_event_filter_check::extract(evt, len, sanitize_stings);
@@ -420,7 +418,7 @@ json_event_filter_check *jevt_filter_check::allocate_new()
{
jevt_filter_check *chk = new jevt_filter_check();
return (json_event_filter_check *)chk;
return (json_event_filter_check *) chk;
}
std::string k8s_audit_filter_check::index_image(const json &j, std::string &field, std::string &idx)
@@ -429,9 +427,8 @@ std::string k8s_audit_filter_check::index_image(const json &j, std::string &fiel
string image;
try
{
image = j[idx_num].at("image");
try {
image = j[idx_num].at("image");
}
catch(json::out_of_range &e)
{
@@ -473,6 +470,7 @@ std::string k8s_audit_filter_check::index_has_name(const json &j, std::string &f
return string("false");
}
std::string k8s_audit_filter_check::index_query_param(const json &j, std::string &field, std::string &idx)
{
string uri = j;
@@ -491,7 +489,7 @@ std::string k8s_audit_filter_check::index_query_param(const json &j, std::string
{
std::vector<std::string> param_parts = sinsp_split(part, '=');
if(param_parts.size() == 2 && uri::decode(param_parts[0], true) == idx)
if(param_parts.size() == 2 && uri::decode(param_parts[0], true)==idx)
{
return uri::decode(param_parts[1]);
}
@@ -500,6 +498,7 @@ std::string k8s_audit_filter_check::index_query_param(const json &j, std::string
return string("<NA>");
}
std::string k8s_audit_filter_check::index_generic(const json &j, std::string &field, std::string &idx)
{
json item;
@@ -512,8 +511,7 @@ std::string k8s_audit_filter_check::index_generic(const json &j, std::string &fi
{
uint64_t idx_num = (idx.empty() ? 0 : stoi(idx));
try
{
try {
item = j[idx_num];
}
catch(json::out_of_range &e)
@@ -531,7 +529,7 @@ std::string k8s_audit_filter_check::index_select(const json &j, std::string &fie
// Use the suffix of the field to determine which property to
// select from each object.
std::string prop = field.substr(field.find_last_of(".") + 1);
std::string prop = field.substr(field.find_last_of(".")+1);
std::string ret;
@@ -544,8 +542,7 @@ std::string k8s_audit_filter_check::index_select(const json &j, std::string &fie
ret += " ";
}
try
{
try {
ret += json_event_filter_check::json_as_string(obj.at(prop));
}
catch(json::out_of_range &e)
@@ -556,8 +553,7 @@ std::string k8s_audit_filter_check::index_select(const json &j, std::string &fie
}
else
{
try
{
try {
ret = j[stoi(idx)].at(prop);
}
catch(json::out_of_range &e)
@@ -577,8 +573,7 @@ std::string k8s_audit_filter_check::index_privileged(const json &j, std::string
if(!idx.empty())
{
try
{
try {
privileged = j[stoi(idx)].at(jpriv);
}
catch(json::out_of_range &e)
@@ -589,8 +584,7 @@ std::string k8s_audit_filter_check::index_privileged(const json &j, std::string
{
for(auto &container : j)
{
try
{
try {
if(container.at(jpriv))
{
privileged = true;
@@ -627,41 +621,42 @@ k8s_audit_filter_check::k8s_audit_filter_check()
{
m_info = {"ka",
"Access K8s Audit Log Events",
{{"ka.auditid", "The unique id of the audit event"},
{"ka.stage", "Stage of the request (e.g. RequestReceived, ResponseComplete, etc.)"},
{"ka.auth.decision", "The authorization decision"},
{"ka.auth.reason", "The authorization reason"},
{"ka.user.name", "The user name performing the request"},
{"ka.user.groups", "The groups to which the user belongs"},
{"ka.impuser.name", "The impersonated user name"},
{"ka.verb", "The action being performed"},
{"ka.uri", "The request URI as sent from client to server"},
{"ka.uri.param", "The value of a given query parameter in the uri (e.g. when uri=/foo?key=val, ka.uri.param[key] is val).", IDX_REQUIRED, IDX_KEY},
{"ka.target.name", "The target object name"},
{"ka.target.namespace", "The target object namespace"},
{"ka.target.resource", "The target object resource"},
{"ka.target.subresource", "The target object subresource"},
{"ka.req.binding.subjects", "When the request object refers to a cluster role binding, the subject (e.g. account/users) being linked by the binding"},
{"ka.req.binding.subject.has_name", "When the request object refers to a cluster role binding, return true if a subject with the provided name exists", IDX_REQUIRED, IDX_KEY},
{"ka.req.binding.role", "When the request object refers to a cluster role binding, the role being linked by the binding"},
{"ka.req.configmap.name", "If the request object refers to a configmap, the configmap name"},
{"ka.req.configmap.obj", "If the request object refers to a configmap, the entire configmap object"},
{"ka.req.container.image", "When the request object refers to a container, the container's images. Can be indexed (e.g. ka.req.container.image[0]). Without any index, returns the first image", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.container.image.repository", "The same as req.container.image, but only the repository part (e.g. sysdig/falco)", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.container.host_network", "When the request object refers to a container, the value of the hostNetwork flag."},
{"ka.req.container.privileged", "When the request object refers to a container, whether or not any container is run privileged. With an index, return whether or not the ith container is run privileged.", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.role.rules", "When the request object refers to a role/cluster role, the rules associated with the role"},
{"ka.req.role.rules.apiGroups", "When the request object refers to a role/cluster role, the api groups associated with the role's rules. With an index, return only the api groups from the ith rule. Without an index, return all api groups concatenated", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.role.rules.nonResourceURLs", "When the request object refers to a role/cluster role, the non resource urls associated with the role's rules. With an index, return only the non resource urls from the ith rule. Without an index, return all non resource urls concatenated", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.role.rules.verbs", "When the request object refers to a role/cluster role, the verbs associated with the role's rules. With an index, return only the verbs from the ith rule. Without an index, return all verbs concatenated", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.role.rules.resources", "When the request object refers to a role/cluster role, the resources associated with the role's rules. With an index, return only the resources from the ith rule. Without an index, return all resources concatenated", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.service.type", "When the request object refers to a service, the service type"},
{"ka.req.service.ports", "When the request object refers to a service, the service's ports. Can be indexed (e.g. ka.req.service.ports[0]). Without any index, returns all ports", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.volume.hostpath", "If the request object contains volume definitions, whether or not a hostPath volume exists that mounts the specified path from the host (...hostpath[/etc]=true if a volume mounts /etc from the host). The index can be a glob, in which case all volumes are considered to find any path matching the specified glob (...hostpath[/usr/*] would match either /usr/local or /usr/bin)", IDX_REQUIRED, IDX_KEY},
{"ka.resp.name", "The response object name"},
{"ka.response.code", "The response code"},
{"ka.response.reason", "The response reason (usually present only for failures)"},
{"ka.useragent", "The useragent of the client who made the request to the apiserver"}}};
{
{"ka.auditid", "The unique id of the audit event"},
{"ka.stage", "Stage of the request (e.g. RequestReceived, ResponseComplete, etc.)"},
{"ka.auth.decision", "The authorization decision"},
{"ka.auth.reason", "The authorization reason"},
{"ka.user.name", "The user name performing the request"},
{"ka.user.groups", "The groups to which the user belongs"},
{"ka.impuser.name", "The impersonated user name"},
{"ka.verb", "The action being performed"},
{"ka.uri", "The request URI as sent from client to server"},
{"ka.uri.param", "The value of a given query parameter in the uri (e.g. when uri=/foo?key=val, ka.uri.param[key] is val).", IDX_REQUIRED, IDX_KEY},
{"ka.target.name", "The target object name"},
{"ka.target.namespace", "The target object namespace"},
{"ka.target.resource", "The target object resource"},
{"ka.target.subresource", "The target object subresource"},
{"ka.req.binding.subjects", "When the request object refers to a cluster role binding, the subject (e.g. account/users) being linked by the binding"},
{"ka.req.binding.subject.has_name", "When the request object refers to a cluster role binding, return true if a subject with the provided name exists", IDX_REQUIRED, IDX_KEY},
{"ka.req.binding.role", "When the request object refers to a cluster role binding, the role being linked by the binding"},
{"ka.req.configmap.name", "If the request object refers to a configmap, the configmap name"},
{"ka.req.configmap.obj", "If the request object refers to a configmap, the entire configmap object"},
{"ka.req.container.image", "When the request object refers to a container, the container's images. Can be indexed (e.g. ka.req.container.image[0]). Without any index, returns the first image", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.container.image.repository", "The same as req.container.image, but only the repository part (e.g. sysdig/falco)", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.container.host_network", "When the request object refers to a container, the value of the hostNetwork flag."},
{"ka.req.container.privileged", "When the request object refers to a container, whether or not any container is run privileged. With an index, return whether or not the ith container is run privileged.", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.role.rules", "When the request object refers to a role/cluster role, the rules associated with the role"},
{"ka.req.role.rules.apiGroups", "When the request object refers to a role/cluster role, the api groups associated with the role's rules. With an index, return only the api groups from the ith rule. Without an index, return all api groups concatenated", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.role.rules.nonResourceURLs", "When the request object refers to a role/cluster role, the non resource urls associated with the role's rules. With an index, return only the non resource urls from the ith rule. Without an index, return all non resource urls concatenated", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.role.rules.verbs", "When the request object refers to a role/cluster role, the verbs associated with the role's rules. With an index, return only the verbs from the ith rule. Without an index, return all verbs concatenated", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.role.rules.resources", "When the request object refers to a role/cluster role, the resources associated with the role's rules. With an index, return only the resources from the ith rule. Without an index, return all resources concatenated", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.service.type", "When the request object refers to a service, the service type"},
{"ka.req.service.ports", "When the request object refers to a service, the service's ports. Can be indexed (e.g. ka.req.service.ports[0]). Without any index, returns all ports", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.volume.hostpath", "If the request object contains volume definitions, whether or not a hostPath volume exists that mounts the specified path from the host (...hostpath[/etc]=true if a volume mounts /etc from the host). The index can be a glob, in which case all volumes are considered to find any path matching the specified glob (...hostpath[/usr/*] would match either /usr/local or /usr/bin)", IDX_REQUIRED, IDX_KEY},
{"ka.resp.name", "The response object name"},
{"ka.response.code", "The response code"},
{"ka.response.reason", "The response reason (usually present only for failures)"}
}};
{
m_aliases = {
@@ -698,20 +693,21 @@ k8s_audit_filter_check::k8s_audit_filter_check()
{"ka.req.volume.hostpath", {"/requestObject/spec/volumes"_json_pointer, check_hostpath_vols}},
{"ka.resp.name", {"/responseObject/metadata/name"_json_pointer}},
{"ka.response.code", {"/responseStatus/code"_json_pointer}},
{"ka.response.reason", {"/responseStatus/reason"_json_pointer}},
{"ka.useragent", {"/userAgent"_json_pointer}}};
{"ka.response.reason", {"/responseStatus/reason"_json_pointer}}
};
}
}
k8s_audit_filter_check::~k8s_audit_filter_check()
{
}
json_event_filter_check *k8s_audit_filter_check::allocate_new()
{
k8s_audit_filter_check *chk = new k8s_audit_filter_check();
return (json_event_filter_check *)chk;
return (json_event_filter_check *) chk;
}
json_event_filter::json_event_filter()
@@ -766,9 +762,9 @@ std::list<json_event_filter_check::check_info> &json_event_filter_factory::get_f
return m_info;
}
json_event_formatter::json_event_formatter(json_event_filter_factory &json_factory, std::string &format):
m_format(format),
m_json_factory(json_factory)
json_event_formatter::json_event_formatter(json_event_filter_factory &json_factory, std::string &format)
: m_format(format),
m_json_factory(json_factory)
{
parse_format();
}
@@ -781,7 +777,7 @@ std::string json_event_formatter::tostring(json_event *ev)
{
std::string ret;
std::list<std::pair<std::string, std::string>> resolved;
std::list<std::pair<std::string,std::string>> resolved;
resolve_tokens(ev, resolved);
@@ -797,7 +793,7 @@ std::string json_event_formatter::tojson(json_event *ev)
{
nlohmann::json ret;
std::list<std::pair<std::string, std::string>> resolved;
std::list<std::pair<std::string,std::string>> resolved;
resolve_tokens(ev, resolved);
@@ -832,11 +828,11 @@ void json_event_formatter::parse_format()
{
// Skip the %
tformat.erase(0, 1);
json_event_filter_check *chk = (json_event_filter_check *)m_json_factory.new_filtercheck(tformat.c_str());
json_event_filter_check *chk = (json_event_filter_check *) m_json_factory.new_filtercheck(tformat.c_str());
if(!chk)
{
throw falco_exception(string("Could not parse format string \"") + m_format + "\": unknown filtercheck field " + tformat);
throw falco_exception(string ("Could not parse format string \"") + m_format + "\": unknown filtercheck field " + tformat);
}
size = chk->parsed_size();
@@ -856,7 +852,7 @@ void json_event_formatter::parse_format()
// Empty fields are only allowed at the beginning of the string
if(m_tokens.size() > 0)
{
throw falco_exception(string("Could not parse format string \"" + m_format + "\": empty filtercheck field"));
throw falco_exception(string ("Could not parse format string \"" + m_format + "\": empty filtercheck field"));
}
continue;
}
@@ -868,7 +864,7 @@ void json_event_formatter::parse_format()
}
}
void json_event_formatter::resolve_tokens(json_event *ev, std::list<std::pair<std::string, std::string>> &resolved)
void json_event_formatter::resolve_tokens(json_event *ev, std::list<std::pair<std::string,std::string>> &resolved)
{
for(auto tok : m_tokens)
{

View File

@@ -63,9 +63,8 @@ end
-- Permissive for case and for common abbreviations.
priorities = {
Emergency=0, Alert=1, Critical=2, Error=3, Warning=4, Notice=5, Informational=5, Debug=7,
emergency=0, alert=1, critical=2, error=3, warning=4, notice=5, informational=5, debug=7,
EMERGENCY=0, ALERT=1, CRITICAL=2, ERROR=3, WARNING=4, NOTICE=5, INFORMATIONAL=5, DEBUG=7,
INFO=5, info=5
INFO=5
}
--[[
@@ -196,9 +195,7 @@ function split_lines(rules_content)
line = string.sub(rules_content, last_pos, pos-1)
if line ~= "" then
lines[#lines+1] = line
if string.len(line) >= 3 and string.sub(line, 1, 3) == "---" then
-- Document marker, skip
elseif string.sub(line, 1, 1) == '-' then
if string.sub(line, 1, 1) == '-' then
indices[#indices+1] = idx
end
@@ -225,8 +222,9 @@ function split_lines(rules_content)
return lines, indices
end
function get_orig_yaml_obj(rules_lines, row, num_lines)
local ret = ""
function get_context(rules_lines, row, num_lines)
local ret = "---\n"
idx = row
while (idx < (row + num_lines) and idx <= #rules_lines) do
@@ -234,17 +232,16 @@ function get_orig_yaml_obj(rules_lines, row, num_lines)
idx = idx + 1
end
ret = ret.."---"
return ret
end
function build_error(rules_lines, row, num_lines, err)
local ret = err.."\n---\n"..get_orig_yaml_obj(rules_lines, row, num_lines).."---"
return ret
end
local ret = err.."\n"..get_context(rules_lines, row, num_lines)
function build_error_with_context(ctx, err)
local ret = err.."\n---\n"..ctx.."---"
return ret
end
@@ -305,29 +302,24 @@ function load_rules(sinsp_lua_parser,
-- second pass
for i,v in ipairs(rules) do
-- Save back the original object as it appeared in the file. Will be used to provide context.
local context = get_orig_yaml_obj(lines, indices[i], (indices[i+1]-indices[i]))
if (not (type(v) == "table")) then
return false, build_error_with_context(context, "Unexpected element of type " ..type(v)..". Each element should be a yaml associative array.")
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), "Unexpected element of type " ..type(v)..". Each element should be a yaml associative array.")
end
v['context'] = context
if (v['required_engine_version']) then
required_engine_version = v['required_engine_version']
if type(required_engine_version) ~= "number" then
return false, build_error_with_context(v['context'], "Value of required_engine_version must be a number")
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), "Value of required_engine_version must be a number")
end
if falco_rules.engine_version(rules_mgr) < v['required_engine_version'] then
return false, build_error_with_context(v['context'], "Rules require engine version "..v['required_engine_version']..", but engine version is "..falco_rules.engine_version(rules_mgr))
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), "Rules require engine version "..v['required_engine_version']..", but engine version is "..falco_rules.engine_version(rules_mgr))
end
elseif (v['macro']) then
if (v['macro'] == nil or type(v['macro']) == "table") then
return false, build_error_with_context(v['context'], "Macro name is empty")
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), "Macro name is empty")
end
if v['source'] == nil then
@@ -340,7 +332,7 @@ function load_rules(sinsp_lua_parser,
for j, field in ipairs({'condition'}) do
if (v[field] == nil) then
return false, build_error_with_context(v['context'], "Macro must have property "..field)
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), "Macro must have property "..field)
end
end
@@ -353,14 +345,11 @@ function load_rules(sinsp_lua_parser,
if append then
if state.macros_by_name[v['macro']] == nil then
return false, build_error_with_context(v['context'], "Macro " ..v['macro'].. " has 'append' key but no macro by that name already exists")
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), "Macro " ..v['macro'].. " has 'append' key but no macro by that name already exists")
end
state.macros_by_name[v['macro']]['condition'] = state.macros_by_name[v['macro']]['condition'] .. " " .. v['condition']
-- Add the current object to the context of the base macro
state.macros_by_name[v['macro']]['context'] = state.macros_by_name[v['macro']]['context'].."\n"..v['context']
else
state.macros_by_name[v['macro']] = v
end
@@ -368,7 +357,7 @@ function load_rules(sinsp_lua_parser,
elseif (v['list']) then
if (v['list'] == nil or type(v['list']) == "table") then
return false, build_error_with_context(v['context'], "List name is empty")
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), "List name is empty")
end
if state.lists_by_name[v['list']] == nil then
@@ -377,7 +366,7 @@ function load_rules(sinsp_lua_parser,
for j, field in ipairs({'items'}) do
if (v[field] == nil) then
return false, build_error_with_context(v['context'], "List must have property "..field)
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), "List must have property "..field)
end
end
@@ -390,7 +379,7 @@ function load_rules(sinsp_lua_parser,
if append then
if state.lists_by_name[v['list']] == nil then
return false, build_error_with_context(v['context'], "List " ..v['list'].. " has 'append' key but no list by that name already exists")
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), "List " ..v['list'].. " has 'append' key but no list by that name already exists")
end
for j, elem in ipairs(v['items']) do
@@ -403,7 +392,7 @@ function load_rules(sinsp_lua_parser,
elseif (v['rule']) then
if (v['rule'] == nil or type(v['rule']) == "table") then
return false, build_error_with_context(v['context'], "Rule name is empty")
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), "Rule name is empty")
end
-- By default, if a rule's condition refers to an unknown
@@ -428,26 +417,23 @@ function load_rules(sinsp_lua_parser,
-- For append rules, all you need is the condition
for j, field in ipairs({'condition'}) do
if (v[field] == nil) then
return false, build_error_with_context(v['context'], "Rule must have property "..field)
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), "Rule must have property "..field)
end
end
if state.rules_by_name[v['rule']] == nil then
if state.skipped_rules_by_name[v['rule']] == nil then
return false, build_error_with_context(v['context'], "Rule " ..v['rule'].. " has 'append' key but no rule by that name already exists")
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), "Rule " ..v['rule'].. " has 'append' key but no rule by that name already exists")
end
else
state.rules_by_name[v['rule']]['condition'] = state.rules_by_name[v['rule']]['condition'] .. " " .. v['condition']
-- Add the current object to the context of the base rule
state.rules_by_name[v['rule']]['context'] = state.rules_by_name[v['rule']]['context'].."\n"..v['context']
end
else
for j, field in ipairs({'condition', 'output', 'desc', 'priority'}) do
if (v[field] == nil) then
return false, build_error_with_context(v['context'], "Rule must have property "..field)
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), "Rule must have property "..field)
end
end
@@ -476,10 +462,7 @@ function load_rules(sinsp_lua_parser,
end
end
else
-- Remove the context from the table, so the table is exactly what was parsed
local context = v['context']
v['context'] = nil
return false, build_error_with_context(context, "Unknown rule object: "..table.tostring(v))
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), "Unknown rule object: "..table.tostring(v))
end
end
@@ -512,14 +495,14 @@ function load_rules(sinsp_lua_parser,
state.lists[v['list']] = {["items"] = items, ["used"] = false}
end
for _, name in ipairs(state.ordered_macro_names) do
for i, name in ipairs(state.ordered_macro_names) do
local v = state.macros_by_name[name]
local status, ast = compiler.compile_macro(v['condition'], state.macros, state.lists)
if status == false then
return false, build_error_with_context(v['context'], ast)
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), ast)
end
if v['source'] == "syscall" then
@@ -531,7 +514,7 @@ function load_rules(sinsp_lua_parser,
state.macros[v['macro']] = {["ast"] = ast.filter.value, ["used"] = false}
end
for _, name in ipairs(state.ordered_rule_names) do
for i, name in ipairs(state.ordered_rule_names) do
local v = state.rules_by_name[name]
@@ -544,7 +527,7 @@ function load_rules(sinsp_lua_parser,
state.macros, state.lists)
if status == false then
return false, build_error_with_context(v['context'], filter_ast)
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), filter_ast)
end
local evtttypes = {}
@@ -682,7 +665,7 @@ function load_rules(sinsp_lua_parser,
formatter = formats.formatter(v['source'], v['output'])
formats.free_formatter(v['source'], formatter)
else
return false, build_error_with_context(v['context'], "Unexpected type in load_rule: "..filter_ast.type)
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), "Unexpected type in load_rule: "..filter_ast.type)
end
::next_rule::

View File

@@ -17,6 +17,8 @@ limitations under the License.
*/
#include <sstream>
#include "rules.h"
#include "logger.h"

View File

@@ -21,6 +21,7 @@ limitations under the License.
#include <set>
#include <memory>
#include <regex>
#include "sinsp.h"
#include "filter.h"
@@ -57,13 +58,13 @@ class falco_rules
void add_filter(string &rule, std::set<uint32_t> &evttypes, std::set<uint32_t> &syscalls, std::set<string> &tags);
void add_k8s_audit_filter(string &rule, std::set<string> &tags);
void enable_rule(string &rule, bool enabled);
std::string get_context(const std::string &content, uint64_t line, uint64_t column);
lua_parser* m_sinsp_lua_parser;
lua_parser* m_json_lua_parser;
sinsp* m_inspector;
falco_engine *m_engine;
lua_State* m_ls;
string m_lua_load_rules = "load_rules";
string m_lua_ignored_syscalls = "ignored_syscalls";
string m_lua_ignored_events = "ignored_events";

View File

@@ -202,8 +202,19 @@ void falco_ruleset::add(string &name,
}
}
void falco_ruleset::enable(const string &substring, bool enabled, uint16_t ruleset)
void falco_ruleset::enable(const string &pattern, bool enabled, uint16_t ruleset)
{
regex re;
bool match_using_regex = true;
try {
re.assign(pattern);
}
catch (std::regex_error e)
{
match_using_regex = false;
}
while (m_rulesets.size() < (size_t) ruleset + 1)
{
m_rulesets.push_back(new ruleset_filters());
@@ -212,9 +223,14 @@ void falco_ruleset::enable(const string &substring, bool enabled, uint16_t rules
for(const auto &val : m_filters)
{
bool matches;
matches = (substring == "" || (val.first.find(substring) != string::npos));
if(match_using_regex)
{
matches = regex_match(val.first, re);
}
else
{
matches = (val.first.find(pattern) != string::npos);
}
if (matches)
{
if(enabled)

View File

@@ -24,6 +24,7 @@ limitations under the License.
#include <vector>
#include <list>
#include <map>
#include <regex>
#include "sinsp.h"
#include "filter.h"
@@ -47,9 +48,9 @@ public:
// specifying unnecessarily large rulesets will result in
// unnecessarily large vectors.
// Find those rules matching the provided substring and set
// Find those rules matching the provided pattern and set
// their enabled status to enabled.
void enable(const std::string &substring, bool enabled, uint16_t ruleset = 0);
void enable(const std::string &pattern, bool enabled, uint16_t ruleset = 0);
// Find those rules that have a tag in the set of tags and set
// their enabled status to enabled. Note that the enabled

View File

@@ -19,7 +19,7 @@ limitations under the License.
#pragma once
#define FALCO_VERSION "@FALCO_VERSION@"
#define FALCO_VERSION "${FALCO_VERSION}"
#define FALCO_LUA_DIR "${CMAKE_INSTALL_PREFIX}/${FALCO_SHARE_DIR}/lua/"
#define FALCO_SOURCE_DIR "${PROJECT_SOURCE_DIR}"

View File

@@ -76,7 +76,7 @@ static void restart_falco(int signal)
static void usage()
{
printf(
"Falco version: " FALCO_VERSION "\n"
"falco version " FALCO_VERSION "\n"
"Usage: falco [options]\n\n"
"Options:\n"
" -h, --help Print this page\n"
@@ -87,7 +87,7 @@ static void usage()
" --cri <path> Path to CRI socket for container metadata\n"
" Use the specified socket to fetch data from a CRI-compatible runtime\n"
" -d, --daemon Run as a daemon\n"
" -D <substring> Disable any rules with names having the substring <substring>. Can be specified multiple times.\n"
" -D <pattern> Disable any rules matching the regex <pattern>. Can be specified multiple times.\n"
" Can not be specified with -t.\n"
" -e <events_file> Read the events from <events_file> (in .scap format for sinsp events, or jsonl for\n"
" k8s audit events) instead of tapping into live.\n"
@@ -471,9 +471,9 @@ int falco_init(int argc, char **argv)
try
{
set<string> disabled_rule_substrings;
string substring;
string all_rules = "";
set<string> disabled_rule_patterns;
string pattern;
string all_rules = ".*";
set<string> disabled_rule_tags;
set<string> enabled_rule_tags;
@@ -502,8 +502,8 @@ int falco_init(int argc, char **argv)
daemon = true;
break;
case 'D':
substring = optarg;
disabled_rule_substrings.insert(substring);
pattern = optarg;
disabled_rule_patterns.insert(pattern);
break;
case 'e':
trace_filename = optarg;
@@ -604,7 +604,7 @@ int falco_init(int argc, char **argv)
case 0:
if(string(long_options[long_index].name) == "version")
{
printf("Falco version: %s\n", FALCO_VERSION);
printf("falco version %s\n", FALCO_VERSION);
return EXIT_SUCCESS;
}
else if (string(long_options[long_index].name) == "cri")
@@ -781,15 +781,15 @@ int falco_init(int argc, char **argv)
}
// You can't both disable and enable rules
if((disabled_rule_substrings.size() + disabled_rule_tags.size() > 0) &&
if((disabled_rule_patterns.size() + disabled_rule_tags.size() > 0) &&
enabled_rule_tags.size() > 0) {
throw std::invalid_argument("You can not specify both disabled (-D/-T) and enabled (-t) rules");
}
for (auto substring : disabled_rule_substrings)
for (auto pattern : disabled_rule_patterns)
{
falco_logger::log(LOG_INFO, "Disabling rules matching substring: " + substring + "\n");
engine->enable_rule(substring, false);
falco_logger::log(LOG_INFO, "Disabling rules matching pattern: " + pattern + "\n");
engine->enable_rule(pattern, false);
}
if(disabled_rule_tags.size() > 0)
@@ -915,63 +915,6 @@ int falco_init(int argc, char **argv)
goto exit;
}
// If daemonizing, do it here so any init errors will
// be returned in the foreground process.
if (daemon && !g_daemonized) {
pid_t pid, sid;
pid = fork();
if (pid < 0) {
// error
falco_logger::log(LOG_ERR, "Could not fork. Exiting.\n");
result = EXIT_FAILURE;
goto exit;
} else if (pid > 0) {
// parent. Write child pid to pidfile and exit
std::ofstream pidfile;
pidfile.open(pidfilename);
if (!pidfile.good())
{
falco_logger::log(LOG_ERR, "Could not write pid to pid file " + pidfilename + ". Exiting.\n");
result = EXIT_FAILURE;
goto exit;
}
pidfile << pid;
pidfile.close();
goto exit;
}
// if here, child.
// Become own process group.
sid = setsid();
if (sid < 0) {
falco_logger::log(LOG_ERR, "Could not set session id. Exiting.\n");
result = EXIT_FAILURE;
goto exit;
}
// Set umask so no files are world anything or group writable.
umask(027);
// Change working directory to '/'
if ((chdir("/")) < 0) {
falco_logger::log(LOG_ERR, "Could not change working directory to '/'. Exiting.\n");
result = EXIT_FAILURE;
goto exit;
}
// Close stdin, stdout, stderr and reopen to /dev/null
close(0);
close(1);
close(2);
open("/dev/null", O_RDONLY);
open("/dev/null", O_RDWR);
open("/dev/null", O_RDWR);
g_daemonized = true;
}
if (trace_filename.size())
{
// Try to open the trace file as a sysdig
@@ -1036,6 +979,63 @@ int falco_init(int argc, char **argv)
inspector->start_dropping_mode(1);
}
// If daemonizing, do it here so any init errors will
// be returned in the foreground process.
if (daemon && !g_daemonized) {
pid_t pid, sid;
pid = fork();
if (pid < 0) {
// error
falco_logger::log(LOG_ERR, "Could not fork. Exiting.\n");
result = EXIT_FAILURE;
goto exit;
} else if (pid > 0) {
// parent. Write child pid to pidfile and exit
std::ofstream pidfile;
pidfile.open(pidfilename);
if (!pidfile.good())
{
falco_logger::log(LOG_ERR, "Could not write pid to pid file " + pidfilename + ". Exiting.\n");
result = EXIT_FAILURE;
goto exit;
}
pidfile << pid;
pidfile.close();
goto exit;
}
// if here, child.
// Become own process group.
sid = setsid();
if (sid < 0) {
falco_logger::log(LOG_ERR, "Could not set session id. Exiting.\n");
result = EXIT_FAILURE;
goto exit;
}
// Set umask so no files are world anything or group writable.
umask(027);
// Change working directory to '/'
if ((chdir("/")) < 0) {
falco_logger::log(LOG_ERR, "Could not change working directory to '/'. Exiting.\n");
result = EXIT_FAILURE;
goto exit;
}
// Close stdin, stdout, stderr and reopen to /dev/null
close(0);
close(1);
close(2);
open("/dev/null", O_RDONLY);
open("/dev/null", O_RDWR);
open("/dev/null", O_RDWR);
g_daemonized = true;
}
if(outfile != "")
{
inspector->setup_cycle_writer(outfile, rollover_mb, duration_seconds, file_limit, event_limit, compress);