Compare commits

..

4 Commits

Author SHA1 Message Date
Mark Stemm
809d20c294 Merge pull request #246 from draios/dev
Merging for 0.7.0
2017-05-30 13:30:39 -07:00
Mark Stemm
b0ae29c23a Merge branch 'dev' 2017-05-15 11:12:11 -07:00
Mark Stemm
d1b6b2be87 Merge pull request #229 from draios/dev
Merging for 0.6.0
2017-03-29 16:00:06 -07:00
Mark Stemm
e00181d553 Merge pull request #174 from draios/dev
Merging for 0.5.0
2016-12-22 13:25:32 -08:00
55 changed files with 210 additions and 1476 deletions

3
.gitignore vendored
View File

@@ -1,7 +1,6 @@
/build*
*~
*.pyc
test/falco_test.pyc
test/falco_tests.yaml
test/traces-negative
test/traces-positive

View File

@@ -21,7 +21,7 @@ install:
- sudo python setup.py install
- cd ../falco
before_script:
- export KERNELDIR=/lib/modules/$(uname -r)/build
- export KERNELDIR=/lib/modules/$(ls /lib/modules | sort | head -1)/build
script:
- set -e
- export CC="gcc-4.8"

View File

@@ -2,58 +2,9 @@
This file documents all notable changes to Falco. The release numbering uses [semantic versioning](http://semver.org).
## v0.8.1
Released 2017-10-10
### Bug Fixes
* Fix packaging to specify correct built-in config file [[#288](https://github.com/draios/falco/pull/288)]
## v0.8.0
Released 2017-10-10
**Important**: the location for falco's configuration file has moved from `/etc/falco.yaml` to `/etc/falco/falco.yaml`. The default rules file has moved from `/etc/falco_rules.yaml` to `/etc/falco/falco_rules.yaml`. In addition, 0.8.0 has added a _local_ ruls file to `/etc/falco/falco_rules.local.yaml`. See [the documentation](https://github.com/draios/falco/wiki/Falco-Default-and-Local-Rules-Files) for more details.
### Major Changes
* Add the ability to append one list to another list by setting an `append: true` attribute. [[#264](https://github.com/draios/falco/pull/264)]
* Add the ability to append one macro/rule to another list by setting an `append: true` attribute. [[#277](https://github.com/draios/falco/pull/277)]
* Ensure that falco rules/config files are preserved across package upgrades/removes if modified. [[#278](https://github.com/draios/falco/pull/278)]
* Add the notion of a "local" rules file that should contain modifications to the default falco rules file. [[#278](https://github.com/draios/falco/pull/278)]
* When using json output, separately include the individual templated fields in the json object. [[#282](https://github.com/draios/falco/pull/282)]
* Add the ability to keep a file/program pipe handle open across rule notifications. [[#283](https://github.com/draios/falco/pull/283)]
* New argument `-V` validates rules file and immediately exits. [[#286](https://github.com/draios/falco/pull/286)]
### Minor Changes
* Minor updates to falco example programs [[#248](https://github.com/draios/falco/pull/248)] [[#275](https://github.com/draios/falco/pull/275)]
* Also validate macros at rule parse time. [[#257](https://github.com/draios/falco/pull/257)]
* Minor README typo fixes [[#276](https://github.com/draios/falco/pull/276)]
* Add a government CLA (contributor license agreement). [[#263](https://github.com/draios/falco/pull/263)]
* Add ability to only run rules with a priority >= some threshold [[#281](https://github.com/draios/falco/pull/281)]
* Add ability to make output channels unbuffered [[#285](https://github.com/draios/falco/pull/285)]
### Bug Fixes
* Fix installation of falco on OSX [[#252](https://github.com/draios/falco/pull/252)]
* Fix a bug that caused the trailing whitespace of a quoted string to be accidentally removed [[#254](https://github.com/draios/falco/pull/254)]
* When multiple sets of kernel headers are installed, find the one for the running kernel [[#260](https://github.com/draios/falco/pull/260)]
* Allow pathnames in rule/macro conditions to contain '.' characters [[#262](https://github.com/draios/falco/pull/262)]
* Fix a bug where a list named "foo" would be substituted even if it were a substring of a longer word like "my_foo" [[#258](https://github.com/draios/falco/pull/258)]
* Remove extra trailing newlines from rule output strings [[#265](https://github.com/draios/falco/pull/265)]
* Improve build pathnames to avoid relative paths when possible [[#284](https://github.com/draios/falco/pull/284)]
### Rule Changes
* Significant changes to default ruleset to address FPs. These changes resulted from hundreds of hours of use in actual customer environments. [[#247](https://github.com/draios/falco/pull/247)] [[#259](https://github.com/draios/falco/pull/259)]
* Add official gitlab EE docker image to list of known shell spawning images. Thanks @dkerwin! [[#270](https://github.com/draios/falco/pull/270)]
* Add keepalived to list of shell spawning binaries. Thanks @dkerwin! [[#269](https://github.com/draios/falco/pull/269)]
## v0.7.0
Released 2017-05-30
Released 2016-05-30
### Major Changes
@@ -76,7 +27,7 @@ None.
## v0.6.1
Released 2017-05-15
Released 2016-05-15
### Major Changes
@@ -96,7 +47,7 @@ None
## v0.6.0
Released 2017-03-29
Released 2016-03-29
### Major Changes

View File

@@ -7,7 +7,7 @@ if(NOT DEFINED FALCO_VERSION)
endif()
if(NOT DEFINED FALCO_ETC_DIR)
set(FALCO_ETC_DIR "/etc/falco")
set(FALCO_ETC_DIR "/etc")
endif()
if(NOT CMAKE_BUILD_TYPE)
@@ -43,9 +43,7 @@ set(PACKAGE_NAME "falco")
set(PROBE_VERSION "${FALCO_VERSION}")
set(PROBE_NAME "falco-probe")
set(PROBE_DEVICE_NAME "falco")
if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
set(CMAKE_INSTALL_PREFIX /usr CACHE PATH "Default install path" FORCE)
endif()
set(CMAKE_INSTALL_PREFIX /usr)
set(CMD_MAKE make)
@@ -303,7 +301,6 @@ if(NOT USE_BUNDLED_LPEG)
else()
set(LPEG_SRC "${PROJECT_BINARY_DIR}/lpeg-prefix/src/lpeg")
set(LPEG_LIB "${PROJECT_BINARY_DIR}/lpeg-prefix/src/lpeg/build/lpeg.a")
message(STATUS "Using bundled lpeg in '${LPEG_SRC}'")
set(LPEG_DEPENDENCIES "")
if(USE_BUNDLED_LUAJIT)
list(APPEND LPEG_DEPENDENCIES "luajit")
@@ -343,7 +340,6 @@ else()
set(LIBYAML_SRC "${PROJECT_BINARY_DIR}/libyaml-prefix/src/libyaml/src")
set(LIBYAML_LIB "${LIBYAML_SRC}/.libs/libyaml.a")
message(STATUS "Using bundled libyaml in '${LIBYAML_SRC}'")
ExternalProject_Add(libyaml
URL "http://download.draios.com/dependencies/libyaml-0.1.4.tar.gz"
URL_MD5 "4a4bced818da0b9ae7fc8ebc690792a7"
@@ -371,7 +367,6 @@ if(NOT USE_BUNDLED_LYAML)
else()
set(LYAML_SRC "${PROJECT_BINARY_DIR}/lyaml-prefix/src/lyaml/ext/yaml")
set(LYAML_LIB "${LYAML_SRC}/.libs/yaml.a")
message(STATUS "Using bundled lyaml in '${LYAML_SRC}'")
set(LYAML_DEPENDENCIES "")
if(USE_BUNDLED_LUAJIT)
list(APPEND LYAML_DEPENDENCIES "luajit")
@@ -392,19 +387,15 @@ endif()
install(FILES falco.yaml
DESTINATION "${FALCO_ETC_DIR}")
add_subdirectory(rules)
if(CMAKE_SYSTEM_NAME MATCHES "Linux")
add_subdirectory("${SYSDIG_DIR}/driver" "${PROJECT_BINARY_DIR}/driver")
endif()
add_subdirectory("${SYSDIG_DIR}/userspace/libscap" "${PROJECT_BINARY_DIR}/userspace/libscap")
add_subdirectory("${SYSDIG_DIR}/userspace/libsinsp" "${PROJECT_BINARY_DIR}/userspace/libsinsp")
set(FALCO_SINSP_LIBRARY sinsp)
set(FALCO_SHARE_DIR share/falco)
set(FALCO_ABSOLUTE_SHARE_DIR "${CMAKE_INSTALL_PREFIX}/${FALCO_SHARE_DIR}")
set(FALCO_BIN_DIR bin)
add_subdirectory(scripts)
set(FALCO_SINSP_LIBRARY sinsp)
set(FALCO_SHARE_DIR ${CMAKE_INSTALL_PREFIX}/share/falco)
add_subdirectory(userspace/engine)
add_subdirectory(userspace/falco)
@@ -425,7 +416,7 @@ set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Sysdig <support@sysdig.com>")
set(CPACK_DEBIAN_PACKAGE_SECTION "utils")
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_DEBIAN_PACKAGE_CONTROL_EXTRA "${CMAKE_BINARY_DIR}/scripts/debian/postinst;${CMAKE_BINARY_DIR}/scripts/debian/prerm;${PROJECT_SOURCE_DIR}/scripts/debian/postrm")
set(CPACK_RPM_PACKAGE_LICENSE "GPLv2")
set(CPACK_RPM_PACKAGE_URL "http://www.sysdig.org")

View File

@@ -2,7 +2,7 @@
#### Latest release
**v0.8.1**
**v0.7.0**
Read the [change log](https://github.com/draios/falco/blob/dev/CHANGELOG.md)
Dev Branch: [![Build Status](https://travis-ci.org/draios/falco.svg?branch=dev)](https://travis-ci.org/draios/falco)<br />
@@ -33,9 +33,10 @@ Documentation
Join the Community
---
* Contact the [official mailing list](https://groups.google.com/forum/#!forum/falco) for support and to talk with other users.
* Follow us on [Twitter](https://twitter.com/sysdig) for general falco and sysdig news.
* This is our [blog](https://sysdig.com/blog/), where you can find the latest [falco](https://sysdig.com/blog/tag/falco/) posts.
* Join our [Public Slack](https://slack.sysdig.com) channel for sysdig and falco announcements and discussions.
* Join our [Public Slack](https://sysdig.slack.com) channel for sysdig and falco announcements and discussions.
License Terms
---
@@ -60,27 +61,15 @@ As always, we are grateful for your past and present contributions to falco.
**Individual contributions**: Individuals who wish to make contributions must review the [Individual Contributor License Agreement](./cla/falco_contributor_agreement.txt) and indicate agreement by adding the following line to every GIT commit message:
```
falco-CLA-1.0-signed-off-by: Joe Smith <joe.smith@email.com>
```
Use your real name; pseudonyms or anonymous contributions are not allowed.
**Corporate contributions**: Employees of corporations, members of LLCs or LLPs, or others acting on behalf of a contributing entity, must review the [Corporate Contributor License Agreement](./cla/falco_corp_contributor_agreement.txt), must be an authorized representative of the contributing entity, and indicate agreement to it on behalf of the contributing entity by adding the following lines to every GIT commit message:
```
falco-CLA-1.0-contributing-entity: Full Legal Name of Entity
falco-CLA-1.0-signed-off-by: Joe Smith <joe.smith@email.com>
```
Use a real name of a natural person who is an authorized representative of the contributing entity; pseudonyms or anonymous contributions are not allowed.
**Government contributions**: Employees or officers of the United States Government, must review the [Government Contributor License Agreement](https://github.com/draios/falco/blob/dev/cla/falco_govt_contributor_agreement.txt), must be an authorized representative of the contributing entity, and indicate agreement to it on behalf of the contributing entity by adding the following lines to every GIT commit message:
```
falco-CLA-1.0-contributing-govt-entity: Full Legal Name of Entity
falco-CLA-1.0-signed-off-by: Joe Smith <joe.smith@email.com>
This file is a work of authorship of an employee or officer of the United States Government and is not subject to copyright in the United States under 17 USC 105.
falco-CLA-1.0-contributing-entity: Full Legal Name of Entity
falco-CLA-1.0-signed-off-by: Joe Smith <joe.smith@email.com>
```
Use a real name of a natural person who is an authorized representative of the contributing entity; pseudonyms or anonymous contributions are not allowed.

View File

@@ -1,33 +0,0 @@
DRAIOS, INC. <20> OPEN SOURCE CONTRIBUTION AGREEMENT FOR UNITED STATES GOVERNMENT CONTRIBUTING ENTITIES (<28>Agreement<6E>)
Draios, Inc. (<28>Draios<6F> or <20>Sysdig<69>) welcomes the work of others on our open source software projects. To contribute code back to our repositories, we require a contributing entity that is a United States Government agency to complete, and agree to, the Government Contributor Agreement (GCA) set forth here, by and through a designated authorized representative. This agreement clarifies the ability for us to use and incorporate the contributions of a government contributing entity in our projects and products. After agreeing to these terms, a contributing entity may contribute to our projects. To indicate the agreement of the contributing entity, an authorized representative shall follow the procedure set forth below under TO AGREE, after reading this Agreement. A <20>contributing entity<74> means any agency or unit of the United States government. We provide a separate CLA for individual contributors.
You accept and agree to the following terms and conditions for Your present and future Contributions that are submitted to Draios/Sysdig.
1. Definitions. "You" (or "Your") shall mean the contributing entity that has authored or otherwise has the right to contribute the Contribution, and that is making this Agreement with Draios/Sysdig. "Contribution" shall mean any original work of authorship, including any modifications or additions to an existing work, that is intentionally submitted by You to Draios/Sysdig for inclusion in, or documentation of, any of the products owned or managed by Draios/Sysdig (the "Work"). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to Draios/Sysdig or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, Draios/Sysdig for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution."
2. Contributions Not Subject to Copyright. Each Contribution is a work authored by the United States Government or an employee or officer thereof and is not subject to copyright under 17 U.S.C. 105.
3. Grant of Patent License. Subject to the terms and conditions of this Agreement, You hereby grant to Draios/Sysdig and to recipients of software distributed by Draios/Sysdig a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims that You have the right to license and that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which such Contribution(s) was submitted. If any entity other than Draios/Sysdig institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that your Contribution, or the Work to which you have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed.
4. You represent to Draios/Sysdig that You own or have the right to contribute Your Contributions to Draios/Sysdig, and that You are legally entitled to grant the license set forth above.
5. You represent that each of Your Contributions is Your original creation (see section 7 for submissions on behalf of others). You represent that Your Contribution submissions include complete details of any third-party license or other restriction (including, but not limited to, related patents and trademarks) of which You are personally aware and which are associated with any part of Your Contributions. You represent that Your sign-off indicating assent to this Agreement includes the real name of a natural person who is an authorized representative of You, and not a pseudonym, and that You are not attempting or making an anonymous Contribution.
6. You are not expected to provide support for Your Contributions, except to the extent You desire to provide support. You may provide support for free, for a fee, or not at all. Unless required by applicable law or agreed to in writing, You provide Your Contributions to Draios/Sysdig on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON- INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE.
7. If You wish to submit work that is not Your original creation, You may submit it to Draios/Sysdig separately from any Contribution, identifying the complete details of its source and of any license or other restriction (including, but not limited to, related patents, trademarks, and license agreements) of which You are aware, and conspicuously marking the work as "Submitted on behalf of a third-party: [named here]".
8. You agree to notify Draios/Sysdig of any facts or circumstances of which you become aware that would make these representations inaccurate in any respect.
9. You understand and agree that this project and Your Contribution are public and that a record of the contribution, including all personal information that You submit with it, including the sign-off of Your authorized representative, may be stored by Draios/Sysdig indefinitely and may be redistributed to others. You understand and agree that Draios/Sysdig has no obligation to use any Contribution in any Draios/Sysdig project or product, and Draios/Sysdig may decline to accept Your Contributions or Draios/Sysdig may remove Your Contributions from Draios/Sysdig projects or products at any time without notice. You understand and agree that Draios/Sysdig is not and will not pay You any form of compensation, in currency, equity or otherwise, in exchange for Your Contributions or for Your assent to this Agreement. You understand and agree that You are independent of Draios/Sysdig and You are not, by entering into this Agreement or providing Your Contributions, becoming employed, hired as an independent contractor, or forming any other relationship with Draios/Sysdig relating to employment, compensation or ownership or involving any fiduciary obligation.
TO AGREE:
Add the following lines to every GIT commit message:
falco-CLA-1.0-contributing-govt-entity: Full Legal Name of Entity
falco-CLA-1.0-signed-off-by: Joe Smith joe.smith@email.com
This file is a work of authorship of an employee or officer of the United States Government and is not subject to copyright in the United States under 17 USC 105.
Use a real name of a natural person who is an authorized representative of the contributing entity; pseudonyms or anonymous contributions are not allowed.

View File

@@ -1,3 +0,0 @@
/etc/falco/falco.yaml
/etc/falco/falco_rules.yaml
/etc/falco/falco_rules.local.yaml

View File

@@ -5,7 +5,7 @@ express_server:
working_dir: /usr/src/app
command: bash -c "npm install && node server.js"
ports:
- "8181:8181"
- "8080:8080"
volumes:
- ${PWD}:/usr/src/app

View File

@@ -2,19 +2,19 @@ var express = require('express'); // call express
var app = express(); // define our app using express
var child_process = require('child_process');
var port = process.env.PORT || 8181; // set our port
var port = process.env.PORT || 8080; // set our port
// ROUTES FOR OUR API
// =============================================================================
var router = express.Router(); // get an instance of the express Router
// test route to make sure everything is working (accessed at GET http://localhost:8181/api)
// test route to make sure everything is working (accessed at GET http://localhost:8080/api)
router.get('/', function(req, res) {
res.json({ message: 'API available'});
});
router.get('/exec/:cmd', function(req, res) {
var ret = child_process.spawnSync(req.params.cmd, { shell: true});
var ret = child_process.spawnSync(req.params.cmd);
res.send(ret.stdout);
});

View File

@@ -1,15 +1,5 @@
# File(s) containing Falco rules, loaded at startup.
#
# falco_rules.yaml ships with the falco package and is overridden with
# every new software version. falco_rules.local.yaml is only created
# if it doesn't exist. If you want to customize the set of rules, add
# your customizations to falco_rules.local.yaml.
#
# The files will be read in the order presented here, so make sure if
# you have overrides they appear in later files.
rules_file:
- /etc/falco/falco_rules.yaml
- /etc/falco/falco_rules.local.yaml
# File containing Falco rules, loaded at startup.
rules_file: /etc/falco_rules.yaml
# Whether to output events in json or text
json_output: false
@@ -25,16 +15,6 @@ log_syslog: true
# "alert", "critical", "error", "warning", "notice", "info", "debug".
log_level: info
# Minimum rule priority level to load and run. All rules having a
# priority more severe than this level will be loaded/run. Can be one
# of "emergency", "alert", "critical", "error", "warning", "notice",
# "info", "debug".
priority: debug
# Whether or not output to any of the output channels below is
# buffered. Defaults to true
buffered_outputs: true
# A throttling mechanism implemented as a token bucket limits the
# rate of falco notifications. This throttling is controlled by the following configuration
# options:
@@ -57,13 +37,8 @@ outputs:
syslog_output:
enabled: true
# If keep_alive is set to true, the file will be opened once and
# continuously written to, with each output message on its own
# line. If keep_alive is set to false, the file will be re-opened
# for each output message.
file_output:
enabled: false
keep_alive: false
filename: ./events.txt
stdout_output:
@@ -74,15 +49,7 @@ stdout_output:
# program: "jq '{text: .output}' | curl -d @- -X POST https://hooks.slack.com/services/XXX"
# - logging (alternate method than syslog):
# program: logger -t falco-test
# - send over a network connection:
# program: nc host.example.com 80
# If keep_alive is set to true, the program will be started once and
# continuously written to, with each output message on its own
# line. If keep_alive is set to false, the program will be re-spawned
# for each output message.
program_output:
enabled: false
keep_alive: false
program: mail -s "Falco Notification" someone@example.com

View File

@@ -1,10 +1,9 @@
if(NOT DEFINED FALCO_ETC_DIR)
set(FALCO_ETC_DIR "/etc/falco")
set(FALCO_ETC_DIR "/etc")
endif()
if(NOT DEFINED FALCO_RULES_DEST_FILENAME)
set(FALCO_RULES_DEST_FILENAME "falco_rules.yaml")
set(FALCO_LOCAL_RULES_DEST_FILENAME "falco_rules.local.yaml")
endif()
if(DEFINED FALCO_COMPONENT)
@@ -12,18 +11,9 @@ install(FILES falco_rules.yaml
COMPONENT "${FALCO_COMPONENT}"
DESTINATION "${FALCO_ETC_DIR}"
RENAME "${FALCO_RULES_DEST_FILENAME}")
install(FILES falco_rules.local.yaml
COMPONENT "${FALCO_COMPONENT}"
DESTINATION "${FALCO_ETC_DIR}"
RENAME "${FALCO_LOCAL_RULES_DEST_FILENAME}")
else()
install(FILES falco_rules.yaml
DESTINATION "${FALCO_ETC_DIR}"
RENAME "${FALCO_RULES_DEST_FILENAME}")
install(FILES falco_rules.local.yaml
DESTINATION "${FALCO_ETC_DIR}"
RENAME "${FALCO_LOCAL_RULES_DEST_FILENAME}")
DESTINATION "${FALCO_ETC_DIR}"
RENAME "${FALCO_RULES_DEST_FILENAME}")
endif()

View File

@@ -1,13 +0,0 @@
####################
# Your custom rules!
####################
# Add new rules, like this one
# - rule: The program "sudo" is run in a container
# desc: An event will trigger every time you run sudo in a container
# condition: evt.type = execve and evt.dir=< and container.id != host and proc.name = sudo
# output: "Sudo run in container (user=%user.name %container.info parent=%proc.pname cmdline=%proc.cmdline)"
# priority: ERROR
# tags: [users, container]
# Or override/append to any rule, macro, or list from the Default Rules

View File

@@ -14,10 +14,10 @@
# condition: (syscall.type=read and evt.dir=> and fd.type in (file, directory))
- macro: open_write
condition: (evt.type=open or evt.type=openat) and evt.is_open_write=true and fd.typechar='f' and fd.num>=0
condition: (evt.type=open or evt.type=openat) and evt.is_open_write=true and fd.typechar='f'
- macro: open_read
condition: (evt.type=open or evt.type=openat) and evt.is_open_read=true and fd.typechar='f' and fd.num>=0
condition: (evt.type=open or evt.type=openat) and evt.is_open_read=true and fd.typechar='f'
- macro: rename
condition: evt.type = rename
@@ -40,43 +40,23 @@
condition: fd.directory in (/bin, /sbin, /usr/bin, /usr/sbin)
- macro: bin_dir_mkdir
condition: >
evt.arg[0] startswith /bin/ or
evt.arg[0] startswith /sbin/ or
evt.arg[0] startswith /usr/bin/ or
evt.arg[0] startswith /usr/sbin/
condition: evt.arg[0] startswith /bin/ or evt.arg[0] startswith /sbin/ or evt.arg[0] startswith /usr/bin/ or evt.arg[0] startswith /usr/sbin/
- macro: bin_dir_rename
condition: >
evt.arg[1] startswith /bin/ or
evt.arg[1] startswith /sbin/ or
evt.arg[1] startswith /usr/bin/ or
evt.arg[1] startswith /usr/sbin/
condition: evt.arg[1] startswith /bin/ or evt.arg[1] startswith /sbin/ or evt.arg[1] startswith /usr/bin/ or evt.arg[1] startswith /usr/sbin/
- macro: etc_dir
condition: fd.name startswith /etc
- macro: ubuntu_so_dirs
condition: >
fd.name startswith /lib/x86_64-linux-gnu or
fd.name startswith /usr/lib/x86_64-linux-gnu or
fd.name startswith /usr/lib/sudo
condition: fd.name startswith /lib/x86_64-linux-gnu or fd.name startswith /usr/lib/x86_64-linux-gnu or fd.name startswith /usr/lib/sudo
- macro: centos_so_dirs
condition: >
fd.name startswith /lib64 or
fd.name startswith /usr/lib64 or
fd.name startswith /usr/libexec
condition: fd.name startswith /lib64 or fd.name startswith /usr/lib64 or fd.name startswith /usr/libexec
- macro: linux_so_dirs
condition: ubuntu_so_dirs or centos_so_dirs or fd.name=/etc/ld.so.cache
- list: shell_binaries
items: [bash, csh, ksh, sh, tcsh, zsh, dash]
- list: shell_mgmt_binaries
items: [add-shell, remove-shell]
- macro: shell_procs
condition: proc.name in (shell_binaries)
@@ -96,10 +76,7 @@
# dpkg -L login | grep bin | xargs ls -ld | grep -v '^d' | awk '{print $9}' | xargs -L 1 basename | tr "\\n" ","
- list: login_binaries
items: [
login, systemd, '"(systemd)"', systemd-logind, su,
nologin, faillog, lastlog, newgrp, sg
]
items: [login, systemd, '"(systemd)"', systemd-logind, su, nologin, faillog, lastlog, newgrp, sg]
# dpkg -L passwd | grep bin | xargs ls -ld | grep -v '^d' | awk '{print $9}' | xargs -L 1 basename | tr "\\n" ","
- list: passwd_binaries
@@ -110,8 +87,7 @@
gpasswd, chfn, expiry, passwd, vigr, cpgr
]
# repoquery -l shadow-utils | grep bin | xargs ls -ld | grep -v '^d' |
# awk '{print $9}' | xargs -L 1 basename | tr "\\n" ","
# repoquery -l shadow-utils | grep bin | xargs ls -ld | grep -v '^d' | awk '{print $9}' | xargs -L 1 basename | tr "\\n" ","
- list: shadowutils_binaries
items: [
chage, gpasswd, lastlog, newgrp, sg, adduser, deluser, chpasswd,
@@ -123,7 +99,7 @@
items: [setup-backend, dragent, sdchecks]
- list: docker_binaries
items: [docker, dockerd, exe, docker-compose, docker-entrypoi]
items: [docker, dockerd, exe, docker-compose]
- list: k8s_binaries
items: [hyperkube, skydns, kube2sky, exechealthz]
@@ -131,17 +107,6 @@
- list: lxd_binaries
items: [lxd, lxcfs]
# Utility/etc programs known to run on mesos slaves. Truncation
# intentional.
- list: mesos_slave_binaries
items: [mesos-health-ch, mesos-docker-ex, mesos-agent, mesos-slave, mesos-logrotate, mesos-fetcher, mesos-executor, 3dt]
- list: phusion_passenger_binaries
items: [PassengerAgent]
- list: chef_binaries
items: [chef-client]
- list: http_server_binaries
items: [nginx, httpd, httpd-foregroun, lighttpd]
@@ -149,29 +114,19 @@
items: [mysqld]
- list: gitlab_binaries
items: [gitlab-shell, gitlab-mon, gitlab-runner-b, git]
items: [gitlab-shell, gitlab-mon, git]
- macro: server_procs
condition: proc.name in (http_server_binaries, db_server_binaries, docker_binaries, sshd)
# The explicit quotes are needed to avoid the - characters being
# interpreted by the filter expression.
- list: rpm_binaries
items: [dnf, rpm, rpmkey, yum, '"75-system-updat"']
- macro: rpm_procs
condition: proc.name in (rpm_binaries)
- list: deb_binaries
items: [dpkg, dpkg-preconfigu, dpkg-reconfigur, apt, apt-get, aptitude,
frontend, preinst, add-apt-reposit, apt-auto-remova, apt-key,
apt-listchanges, unattended-upgr
]
# The truncated dpkg-preconfigu is intentional, process names are
# truncated at the sysdig level.
- list: package_mgmt_binaries
items: [rpm_binaries, deb_binaries, update-alternat, gem]
items: [
dpkg, dpkg-preconfigu, dnf, rpm, rpmkey, yum, frontend,
apt, apt-get, aptitude, add-apt-reposit, apt-auto-remova, apt-key,
preinst, update-alternat, unattended-upgr
]
- macro: package_mgmt_procs
condition: proc.name in (package_mgmt_binaries)
@@ -187,14 +142,11 @@
- list: userexec_binaries
items: [sudo, su]
- list: known_setuid_binaries
items: [sshd, dbus-daemon-lau, ping, ping6, critical-stack-]
- list: user_mgmt_binaries
items: [login_binaries, passwd_binaries, shadowutils_binaries]
- list: dev_creation_binaries
items: [blkid, rename_device, update_engine]
items: [blkid]
- list: aide_wrapper_binaries
items: [aide.wrapper, update-aide.con]
@@ -202,47 +154,20 @@
- list: hids_binaries
items: [aide]
- list: vpn_binaries
items: [openvpn]
- list: nomachine_binaries
items: [nxexec, nxnode.bin, nxserver.bin, nxclient.bin]
- list: x2go_binaries
items: [x2gosuspend-age, x2goagent]
- list: xray_rabbitmq_binaries
items: ['"1_scheduler"', '"2_scheduler"', '"3_scheduler"', '"4_scheduler"']
- list: nids_binaries
items: [bro, broctl]
- list: monitoring_binaries
items: [icinga2, nrpe, npcd, check_sar_perf., qualys-cloud-ag]
items: [icinga2, nrpe, npcd, check_sar_perf.]
- macro: system_procs
condition: proc.name in (coreutils_binaries, user_mgmt_binaries)
- list: mail_binaries
items: [sendmail, sendmail-msp, postfix, procmail, exim4, pickup, showq, mailq]
- list: sendmail_config_binaries
items: [
update_conf, parse_mc, makemap_hash, newaliases, update_mk, update_tlsm4,
update_db, update_mc, ssmtp.postinst, mailq
]
- list: make_binaries
items: [make, gmake, cmake]
- list: keepalived_binaries
items: [keepalived]
items: [sendmail, sendmail-msp, postfix, procmail, exim4, pickup, showq]
- macro: sensitive_files
condition: >
fd.name startswith /etc and
(fd.name in (/etc/shadow, /etc/sudoers, /etc/pam.conf)
or fd.directory in (/etc/sudoers.d, /etc/pam.d))
condition: fd.name startswith /etc and (fd.name in (/etc/shadow, /etc/sudoers, /etc/pam.conf) or fd.directory in (/etc/sudoers.d, /etc/pam.d))
# Indicates that the process is new. Currently detected using time
# since process was started, using a threshold of 5 seconds.
@@ -253,8 +178,7 @@
- macro: inbound
condition: ((evt.type=listen and evt.dir=>) or (evt.type=accept and evt.dir=<))
# Currently sendto is an ignored syscall, otherwise this could also
# check for (evt.type=sendto and evt.dir=>)
# Currently sendto is an ignored syscall, otherwise this could also check for (evt.type=sendto and evt.dir=>)
- macro: outbound
condition: evt.type=connect and evt.dir=< and (fd.typechar=4 or fd.typechar=6)
@@ -263,10 +187,7 @@
# Ssh
- macro: ssh_error_message
condition: >
(evt.arg.data contains "Invalid user" or
evt.arg.data contains "preauth" or
evt.arg.data contains "Failed password")
condition: (evt.arg.data contains "Invalid user" or evt.arg.data contains "preauth" or evt.arg.data contains "Failed password")
# System
- macro: modules
@@ -282,25 +203,12 @@
# the command line.
- macro: container
condition: container.id != host
- macro: interactive
condition: >
((proc.aname=sshd and proc.name != sshd) or
proc.name=systemd-logind or proc.name=login)
condition: ((proc.aname=sshd and proc.name != sshd) or proc.name=systemd-logind or proc.name=login)
- macro: syslog
condition: fd.name in (/dev/log, /run/systemd/journal/syslog)
- list: cron_binaries
items: [anacron, cron, crond]
# https://github.com/liske/needrestart
- list: needrestart_binaries
items: [needrestart, 10-dpkg, 20-rpm, 30-pacman]
# Possible scripts run by sshkit
- list: sshkit_script_binaries
items: [10_etc_sudoers., 10_passwd_group]
items: [cron, crond]
# System users that should never log into a system. Consider adding your own
# service users (e.g. 'apache' or 'mysqld') here.
@@ -320,102 +228,14 @@
condition: (proc.name in (python, pypy) and proc.cmdline contains ansible)
- macro: python_running_denyhosts
condition: >
(proc.name=python and
(proc.cmdline contains /usr/sbin/denyhosts or
proc.cmdline contains /usr/local/bin/denyhosts.py))
condition: (proc.name=python and (proc.cmdline contains /usr/sbin/denyhosts or proc.cmdline contains /usr/local/bin/denyhosts.py))
- macro: parent_python_running_denyhosts
condition: >
(proc.pname=python and
(proc.pcmdline contains /usr/sbin/denyhosts or
proc.pcmdline contains /usr/local/bin/denyhosts.py))
- macro: parent_python_running_sdchecks
condition: >
(proc.pname in (python, python2.7) and
(proc.pcmdline contains /opt/draios/bin/sdchecks))
condition: (proc.pname=python and (proc.pcmdline contains /usr/sbin/denyhosts or proc.pcmdline contains /usr/local/bin/denyhosts.py))
- macro: parent_bro_running_python
condition: (proc.pname=python and proc.cmdline contains /usr/share/broctl)
- macro: parent_java_running_jenkins
condition: >
(proc.pname=java and proc.pcmdline contains jenkins.war
or proc.pcmdline contains /tmp/slave.jar)
- macro: jenkins_script_sh
condition: (proc.pcmdline startswith "script.sh -xe /var/jenkins_home")
- macro: parent_java_running_echo
condition: (proc.pname=java and proc.cmdline startswith "sh -c echo")
- macro: parent_java_running_sbt
condition: (proc.pname=java and proc.pcmdline contains sbt-launch.jar)
# The crxlsx is a bit different than the other build-like things, but
# close enough to add here rather than create a separate macro.
- macro: parent_scripting_running_builds
condition: >
(proc.pname in (php,php5-fpm,php-fpm7.1,python,ruby,ruby2.3,node) and (
proc.cmdline startswith "sh -c git" or
proc.cmdline startswith "sh -c date" or
proc.cmdline startswith "sh -c /usr/bin/g++" or
proc.cmdline startswith "sh -c /usr/bin/gcc" or
proc.cmdline startswith "sh -c gcc" or
proc.cmdline startswith "sh -c if type gcc" or
proc.cmdline startswith "sh -c cd '/var/www/edi/';LC_ALL=en_US.UTF-8 git" or
proc.cmdline startswith "sh -c /usr/src/app/crxlsx/bin/linux/crxlsx" or
proc.pcmdline startswith "node /opt/nodejs/bin/yarn"))
- macro: parent_node_running_npm
condition: proc.pcmdline startswith "node /usr/local/bin/npm"
- macro: parent_nginx_running_serf
condition: (proc.pname=nginx and proc.cmdline startswith "sh -c serf")
- macro: parent_Xvfb_running_xkbcomp
condition: (proc.pname=Xvfb and proc.cmdline startswith 'sh -c "/usr/bin/xkbcomp"')
- macro: mysql_image_running_healthcheck
condition: container.image=mysql and proc.cmdline="sh -c /healthcheck.sh"
- macro: bundle_running_ruby
condition: >
(proc.pname=ruby and (
proc.aname[2]=bundle or
proc.aname[3]=bundle or
proc.aname[4]=bundle))
# Qualys seems to run a variety of shell subprocesses, at various
# levels. This checks at a few levels without the cost of a full
# proc.aname, which traverses the full parent heirarchy.
- macro: run_by_qualys
condition: >
(proc.pname=qualys-cloud-ag or
proc.aname[2]=qualys-cloud-ag or
proc.aname[3]=qualys-cloud-ag or
proc.aname[4]=qualys-cloud-ag)
# Chef is similar.
- macro: run_by_chef
condition: (proc.aname[2]=chef_command_wr or proc.aname[3]=chef_command_wr)
- macro: run_by_adclient
condition: (proc.aname[2]=adclient or proc.aname[3]=adclient or proc.aname[4]=adclient)
- macro: run_by_centrify
condition: (proc.aname[2]=centrify or proc.aname[3]=centrify or proc.aname[4]=centrify)
- macro: run_by_puppet
condition: (proc.aname[2]=puppet or proc.aname[3]=puppet)
- macro: run_by_h2o
condition: (proc.pname=perl and proc.aname[2]=h2o)
- macro: run_by_passenger_agent
condition: (proc.pname=ruby and proc.aname[2]=PassengerAgent)
# As a part of kernel upgrades, dpkg will spawn a perl script with the
# name linux-image-N.N. This macro matches that.
- macro: parent_linux_image_upgrade_script
@@ -431,68 +251,27 @@
- rule: Write below binary dir
desc: an attempt to write to any file below a set of binary directories
condition: bin_dir and evt.dir = < and open_write and not package_mgmt_procs
output: >
File below a known binary directory opened for writing (user=%user.name
command=%proc.cmdline file=%fd.name)
output: "File below a known binary directory opened for writing (user=%user.name command=%proc.cmdline file=%fd.name)"
priority: ERROR
tags: [filesystem]
- list: safe_etc_dirs
items: [/etc/cassandra, /etc/ssl/certs/java, /etc/logstash, /etc/nginx/conf.d, /etc/container_environment, /etc/hrmconfig]
- macro: fluentd_writing_conf_files
condition: (proc.name=start-fluentd and fd.name in (/etc/fluent/fluent.conf, /etc/td-agent/td-agent.conf))
- macro: qualys_writing_conf_files
condition: (proc.name=qualys-cloud-ag and fd.name=/etc/qualys/cloud-agent/qagent-log.conf)
- macro: git_writing_nssdb
condition: (proc.cmdline="git-remote-http origin" and fd.directory=/etc/pki/nssdb)
# Add conditions to this macro (probably in a separate file,
# overwriting this macro) to allow for specific combinations of
# programs writing below specific directories below
# /etc. fluentd_writing_conf_files is a good example to follow, as it
# specifies both the program doing the writing as well as the specific
# files it is allowed to modify.
#
# In this file, it just takes one of the programs in the base macro
# and repeats it.
- macro: user_known_write_etc_conditions
condition: proc.name=confd
- macro: write_etc_common
condition: >
etc_dir and evt.dir = < and open_write
and not proc.name in (passwd_binaries, shadowutils_binaries, sysdigcloud_binaries,
package_mgmt_binaries, ssl_mgmt_binaries, dhcp_binaries,
dev_creation_binaries, shell_mgmt_binaries,
sendmail_config_binaries,
sshkit_script_binaries,
ldconfig.real, ldconfig, confd, gpg, insserv,
apparmor_parser, update-mime, tzdata.config, tzdata.postinst,
systemd, systemd-machine, systemd-sysuser,
debconf-show, rollerd, bind9.postinst, sv,
gen_resolvconf., update-ca-certi, certbot, runsv,
qualys-cloud-ag, locales.postins, nomachine_binaries,
adclient, certutil, crlutil)
and not proc.pname in (sysdigcloud_binaries, sendmail_config_binaries, hddtemp.postins, sshkit_script_binaries)
and not fd.name pmatch (safe_etc_dirs)
and not fd.name in (/etc/container_environment.sh, /etc/container_environment.json, /etc/motd, /etc/motd.svc)
systemd-machine, debconf-show, rollerd, bind9.postinst, sv)
and not proc.pname in (sysdigcloud_binaries)
and not fd.directory in (/etc/cassandra, /etc/ssl/certs/java)
and not ansible_running_python
and not python_running_denyhosts
and not fluentd_writing_conf_files
and not user_known_write_etc_conditions
and not run_by_centrify
and not run_by_adclient
and not qualys_writing_conf_files
and not git_writing_nssdb
- rule: Write below etc
desc: an attempt to write to any file below /etc, not in a pipe installer session
condition: write_etc_common and not proc.sname=fbash
output: "File below /etc opened for writing (user=%user.name command=%proc.cmdline parent=%proc.pname file=%fd.name name=%proc.name gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4])"
output: "File below /etc opened for writing (user=%user.name command=%proc.cmdline file=%fd.name)"
priority: ERROR
tags: [filesystem]
@@ -500,9 +279,7 @@
- rule: Write below etc in installer
desc: an attempt to write to any file below /etc, in a pipe installer session
condition: write_etc_common and proc.sname=fbash
output: >
File below /etc opened for writing (user=%user.name command=%proc.cmdline
file=%fd.name) within pipe installer session
output: "File below /etc opened for writing (user=%user.name command=%proc.cmdline file=%fd.name) within pipe installer session"
priority: INFO
tags: [filesystem]
@@ -510,91 +287,53 @@
condition: proc.name in (cmp, cp) and proc.pname=passwd
- rule: Read sensitive file trusted after startup
desc: >
an attempt to read any sensitive file (e.g. files containing user/password/authentication
information) by a trusted program after startup. Trusted programs might read these files
at startup to load initial state, but not afterwards.
desc: an attempt to read any sensitive file (e.g. files containing user/password/authentication information) by a trusted program after startup. Trusted programs might read these files at startup to load initial state, but not afterwards.
condition: sensitive_files and open_read and server_procs and not proc_is_new and proc.name!="sshd"
output: >
Sensitive file opened for reading by trusted program after startup (user=%user.name
command=%proc.cmdline parent=%proc.pname file=%fd.name parent=%proc.pname gparent=%proc.aname[2])
output: "Sensitive file opened for reading by trusted program after startup (user=%user.name command=%proc.cmdline file=%fd.name)"
priority: WARNING
tags: [filesystem]
- list: read_sensitive_file_binaries
items: [
iptables, ps, lsb_release, check-new-relea, dumpe2fs, accounts-daemon, sshd,
vsftpd, systemd, mysql_install_d, psql
]
# Add conditions to this macro (probably in a separate file,
# overwriting this macro) to allow for specific combinations of
# programs accessing sensitive files.
# fluentd_writing_conf_files is a good example to follow, as it
# specifies both the program doing the writing as well as the specific
# files it is allowed to modify.
#
# In this file, it just takes one of the macros in the base rule
# and repeats it.
- macro: user_read_sensitive_file_conditions
condition: cmp_cp_by_passwd
items: [iptables, ps, lsb_release, check-new-relea, dumpe2fs, accounts-daemon, sshd, vsftpd, systemd]
- rule: Read sensitive file untrusted
desc: >
an attempt to read any sensitive file (e.g. files containing user/password/authentication
information). Exceptions are made for known trusted programs.
desc: an attempt to read any sensitive file (e.g. files containing user/password/authentication information). Exceptions are made for known trusted programs.
condition: >
sensitive_files and open_read
and not proc.name in (user_mgmt_binaries, userexec_binaries, package_mgmt_binaries,
cron_binaries, read_sensitive_file_binaries, shell_binaries, hids_binaries,
vpn_binaries, sendmail_config_binaries, nomachine_binaries, sshkit_script_binaries)
and not proc.name in (user_mgmt_binaries, userexec_binaries, package_mgmt_binaries, cron_binaries, read_sensitive_file_binaries, shell_binaries, hids_binaries)
and not cmp_cp_by_passwd
and not ansible_running_python
and not proc.cmdline contains /usr/bin/mandb
and not run_by_qualys
and not run_by_chef
and not user_read_sensitive_file_conditions
output: >
Sensitive file opened for reading by non-trusted program (user=%user.name name=%proc.name
command=%proc.cmdline file=%fd.name parent=%proc.pname gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4])
output: "Sensitive file opened for reading by non-trusted program (user=%user.name name=%proc.name command=%proc.cmdline file=%fd.name)"
priority: WARNING
tags: [filesystem]
# Only let rpm-related programs write to the rpm database
- rule: Write below rpm database
desc: an attempt to write to the rpm database by any non-rpm related program
condition: fd.name startswith /var/lib/rpm and open_write and not rpm_procs and not ansible_running_python
condition: fd.name startswith /var/lib/rpm and open_write and not proc.name in (dnf,rpm,rpmkey,yum) and not ansible_running_python
output: "Rpm database opened for writing by a non-rpm program (command=%proc.cmdline file=%fd.name)"
priority: ERROR
tags: [filesystem, software_mgmt]
- rule: DB program spawned process
desc: >
a database-server related program spawned a new process other than itself.
This shouldn\'t occur and is a follow on from some SQL injection attacks.
desc: a database-server related program spawned a new process other than itself. This shouldn\'t occur and is a follow on from some SQL injection attacks.
condition: proc.pname in (db_server_binaries) and spawned_process and not proc.name in (db_server_binaries)
output: >
Database-related program spawned process other than itself (user=%user.name
program=%proc.cmdline parent=%proc.pname)
output: "Database-related program spawned process other than itself (user=%user.name program=%proc.cmdline parent=%proc.pname)"
priority: NOTICE
tags: [process, database]
- rule: Modify binary dirs
desc: an attempt to modify any file below a set of binary directories.
condition: bin_dir_rename and modify and not package_mgmt_procs
output: >
File below known binary directory renamed/removed (user=%user.name command=%proc.cmdline
operation=%evt.type file=%fd.name %evt.args)
output: "File below known binary directory renamed/removed (user=%user.name command=%proc.cmdline operation=%evt.type file=%fd.name %evt.args)"
priority: ERROR
tags: [filesystem]
- rule: Mkdir binary dirs
desc: an attempt to create a directory below a set of binary directories.
condition: mkdir and bin_dir_mkdir and not package_mgmt_procs
output: >
Directory below known binary directory created (user=%user.name
command=%proc.cmdline directory=%evt.arg.path)
output: "Directory below known binary directory created (user=%user.name command=%proc.cmdline directory=%evt.arg.path)"
priority: ERROR
tags: [filesystem]
@@ -609,43 +348,32 @@
# Temporarily disabling this rule as it's tripping over https://github.com/draios/sysdig/issues/598
# - rule: Syscall returns eaccess
# desc: >
# any system call that returns EACCESS. This is not always a strong
# indication of a problem, hence the INFO priority.
# desc: any system call that returns EACCESS. This is not always a strong indication of a problem, hence the INFO priority.
# condition: evt.res = EACCESS
# output: >
# System call returned EACCESS (user=%user.name command=%proc.cmdline
# syscall=%evt.type args=%evt.args)
# output: "System call returned EACCESS (user=%user.name command=%proc.cmdline syscall=%evt.type args=%evt.args)"
# priority: INFO
- rule: Change thread namespace
desc: >
an attempt to change a program/thread\'s namespace (commonly done
as a part of creating a container) by calling setns.
desc: an attempt to change a program/thread\'s namespace (commonly done as a part of creating a container) by calling setns.
condition: >
evt.type = setns
and not proc.name in (docker_binaries, k8s_binaries, lxd_binaries, sysdigcloud_binaries, sysdig, nsenter)
and not proc.name startswith "runc:"
and not proc.pname in (sysdigcloud_binaries)
and not java_running_sdjagent
output: >
Namespace change (setns) by unexpected program (user=%user.name command=%proc.cmdline
parent=%proc.pname %container.info)
output: "Namespace change (setns) by unexpected program (user=%user.name command=%proc.cmdline parent=%proc.pname %container.info)"
priority: NOTICE
tags: [process]
- list: known_shell_spawn_binaries
items: [
sshd, sudo, su, tmux, screen, emacs, systemd, login, flock, fbash,
nginx, monit, supervisord, dragent, aws, awslogs, initdb, docker-compose,
configure, awk, falco, fail2ban-server, fleetctl,
nginx, monit, supervisord, dragent, aws, initdb, docker-compose,
make, configure, awk, falco, fail2ban-server, fleetctl,
logrotate, ansible, less, adduser, pycompile, py3compile,
pyclean, py3clean, pip, pip2, ansible-playboo, man-db,
init, pluto, mkinitramfs, unattended-upgr, watch, sysdig,
landscape-sysin, nessusd, PM2, syslog-summary, erl_child_setup,
npm, cloud-init, toybox, ceph, hhvm, certbot, mysql_install_d,
serf, a2enmod, runsv, supervisord, varnishd, authconfig, tini,
timeout, updatedb.findut, mysql_ssl_rsa_s, adclient, systemd-udevd
landscape-sysin, nessusd, PM2, syslog-summary, erl_child_setup
]
- rule: Run shell untrusted
@@ -654,33 +382,14 @@
spawned_process and not container
and shell_procs
and proc.pname exists
and not proc.pname in (cron_binaries, shell_binaries, make_binaries, known_shell_spawn_binaries, docker_binaries,
and not proc.pname in (cron_binaries, shell_binaries, known_shell_spawn_binaries, docker_binaries,
k8s_binaries, package_mgmt_binaries, aide_wrapper_binaries, nids_binaries,
monitoring_binaries, gitlab_binaries, mesos_slave_binaries,
keepalived_binaries,
needrestart_binaries, phusion_passenger_binaries, chef_binaries, nomachine_binaries,
x2go_binaries)
monitoring_binaries)
and not parent_ansible_running_python
and not parent_bro_running_python
and not parent_python_running_denyhosts
and not parent_python_running_sdchecks
and not parent_linux_image_upgrade_script
and not parent_java_running_jenkins
and not jenkins_script_sh
and not parent_java_running_echo
and not parent_scripting_running_builds
and not parent_Xvfb_running_xkbcomp
and not parent_nginx_running_serf
and not parent_node_running_npm
and not parent_java_running_sbt
and not run_by_chef
and not run_by_puppet
and not run_by_adclient
and not run_by_centrify
output: >
Shell spawned by untrusted binary (user=%user.name shell=%proc.name parent=%proc.pname
cmdline=%proc.cmdline pcmdline=%proc.pcmdline gparent=%proc.aname[2] ggparent=%proc.aname[3]
gggparent=%proc.aname[4] ggggparent=%proc.aname[5])
output: "Shell spawned by untrusted binary (user=%user.name shell=%proc.name parent=%proc.pname cmdline=%proc.cmdline pcmdline=%proc.pcmdline)"
priority: DEBUG
tags: [host, shell]
@@ -688,51 +397,25 @@
condition: (container.image startswith sysdig/agent or
(container.image startswith sysdig/falco and
not container.image startswith sysdig/falco-event-generator) or
container.image startswith quay.io/sysdig or
container.image startswith sysdig/sysdig or
container.image startswith gcr.io/google_containers/hyperkube or
container.image startswith quay.io/coreos/flannel or
container.image startswith gcr.io/google_containers/kube-proxy or
container.image startswith calico/node)
container.image startswith gcr.io/google_containers/kube-proxy)
# These containers are ones that are known to spawn lots of
# shells. Generally, they are for systems where the container is used
# as a packaging mechanism more than for a dedicated microservice.
- macro: shell_spawning_containers
condition: (container.image startswith jenkins or
container.image startswith gitlab/gitlab-ce or
container.image startswith gitlab/gitlab-ee)
- rule: Launch Privileged Container
desc: Detect the initial process started in a privileged container. Exceptions are made for known trusted images.
condition: evt.type=execve and proc.vpid=1 and container and container.privileged=true and not trusted_containers
output: Privileged container started (user=%user.name command=%proc.cmdline %container.info)
- rule: File Open by Privileged Container
desc: Any open by a privileged container. Exceptions are made for known trusted images.
condition: (open_read or open_write) and container and container.privileged=true and not trusted_containers
output: File opened for read/write by privileged container (user=%user.name command=%proc.cmdline %container.info file=%fd.name)
priority: INFO
tags: [container, cis]
- macro: sensitive_mount
condition: (container.mount.dest[/proc*] != "N/A")
# The steps libcontainer performs to set up the root program for a container are:
# - clone + exec self to a program runc:[0:PARENT]
# - clone a program runc:[1:CHILD] which sets up all the namespaces
# - clone a second program runc:[2:INIT] + exec to the root program.
# The parent of runc:[2:INIT] is runc:0:PARENT]
# As soon as 1:CHILD is created, 0:PARENT exits, so there's a race
# where at the time 2:INIT execs the root program, 0:PARENT might have
# already exited, or might still be around. So we handle both.
# We also let runc:[1:CHILD] count as the parent process, which can occur
# 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], docker-runc))
- rule: Launch Sensitive Mount Container
desc: >
Detect the initial process started by a container that has a mount from a sensitive host directory
(i.e. /proc). Exceptions are made for known trusted images.
condition: evt.type=execve and proc.vpid=1 and container and sensitive_mount and not trusted_containers
output: Container with sensitive mount started (user=%user.name command=%proc.cmdline %container.info)
- rule: Sensitive Mount by Container
desc: Any open by a container that has a mount from a sensitive host directory (i.e. /proc). Exceptions are made for known trusted images.
condition: (open_read or open_write) and container and sensitive_mount and not trusted_containers
output: File opened for read/write by container mounting sensitive directory (user=%user.name command=%proc.cmdline %container.info file=%fd.name)
priority: INFO
tags: [container, cis]
@@ -753,119 +436,28 @@
condition: >
spawned_process and container
and shell_procs and proc.tty != 0
output: >
A shell was spawned in a container with an attached terminal (user=%user.name %container.info
shell=%proc.name parent=%proc.pname cmdline=%proc.cmdline terminal=%proc.tty)
output: "A shell was spawned in a container with an attached terminal (user=%user.name %container.info shell=%proc.name parent=%proc.pname cmdline=%proc.cmdline terminal=%proc.tty)"
priority: NOTICE
tags: [container, shell]
# For some container types (mesos), there isn't a container image to
# work with, and the container name is autogenerated, so there isn't
# any stable aspect of the software to work with. In this case, we
# fall back to allowing certain command lines.
- list: known_container_shell_spawn_cmdlines
items: [
'"bash -c curl -f localhost:$API_PORT/admin/healthcheck"',
'"sh -c curl http://localhost:6060/debug/vars>/dev/null "',
'"sh -c curl http://localhost:6060/debug/vars>/dev/null"',
'"sh -c curl http://localhost:6060/debug/vars>/dev/null"',
'"sh -c curl http://localhost:6060/debug/vars>/dev/null "',
'"sh -c pgrep java && exit 0 || exit 1 "',
'"sh -c uname -p 2> /dev/null"',
'"sh -c uname -s 2>&1"',
'"sh -c uname -r 2>&1"',
'"sh -c uname -v 2>&1"',
'"sh -c uname -a 2>&1"',
'"sh -c ruby -v 2>&1"',
'"sh -c echo healthy "',
'"sh -c echo alive "',
'"sh -c getconf CLK_TCK"',
'"sh -c getconf PAGESIZE"',
'"sh -c LC_ALL=C LANG=C /sbin/ldconfig -p 2>/dev/null"',
'"sh -c /sbin/ldconfig -p 2>/dev/null"',
'"sh -c stty -a 2>/dev/null"',
'"sh -c node index.js"',
'"sh -c node index"',
'"sh -c node ./src/start.js"',
'"sh -c node app.js"',
'"sh -c node -e \"require(''nan'')\")"',
'"sh -c node $NODE_DEBUG_OPTION index.js "',
'"sh -c crontab -l 2"',
'"sh -c lsb_release -a"',
'"sh -c whoami"',
'"sh -c node_modules/.bin/bower-installer"'
]
# This list allows for easy additions to the set of commands allowed
# to run shells in containers without having to without having to copy
# and override the entire run shell in container macro. Once
# https://github.com/draios/falco/issues/255 is fixed this will be a
# bit easier, as someone could append of any of the existing lists.
- list: user_known_container_shell_spawn_binaries
items: []
# This macro allows for easy additions to the set of commands allowed
# to run shells in containers without having to override the entire
# rule. Its default value is an expression that always is false, which
# becomes true when the "not ..." in the rule is applied.
- macro: user_shell_container_exclusions
condition: (evt.num=0)
# Temporarily adding as an example
- macro: node_running_edi_dynamodb
condition: >
(proc.pname=node and (proc.pcmdline contains /var/www/edi/process.js or
proc.pcmdline contains "sh -c /var/www/edi/bin/sftp.sh"))
- rule: Run shell in container
desc: a shell was spawned by a non-shell program in a container. Container entrypoints are excluded.
condition: >
spawned_process and container
and shell_procs
and not container_entrypoint
and not proc.pname in (shell_binaries, make_binaries, docker_binaries, k8s_binaries, package_mgmt_binaries,
lxd_binaries, mesos_slave_binaries, aide_wrapper_binaries, nids_binaries,
user_known_container_shell_spawn_binaries,
needrestart_binaries,
phusion_passenger_binaries,
chef_binaries,
nomachine_binaries,
x2go_binaries,
xray_rabbitmq_binaries,
monitoring_binaries, gitlab_binaries, initdb, pg_ctl, awk, falco, cron,
erl_child_setup, ceph, PM2, pycompile, py3compile, hhvm, npm, mysql_install_d, serf,
runsv, supervisord, varnishd, crond, logrotate, timeout, tini,
xrdb, xfce4-session, weave, mysql_ssl_rsa_s, logdna-agent, bundle, configure)
and proc.pname exists
and not proc.pname in (shell_binaries, docker_binaries, k8s_binaries, lxd_binaries, aide_wrapper_binaries, nids_binaries,
monitoring_binaries, gitlab_binaries, initdb, pg_ctl, awk, falco, cron, erl_child_setup)
and not trusted_containers
and not shell_spawning_containers
and not parent_java_running_echo
and not parent_scripting_running_builds
and not parent_Xvfb_running_xkbcomp
and not mysql_image_running_healthcheck
and not parent_nginx_running_serf
and not proc.cmdline in (known_container_shell_spawn_cmdlines)
and not parent_node_running_npm
and not user_shell_container_exclusions
and not node_running_edi_dynamodb
and not run_by_h2o
and not run_by_passenger_agent
and not parent_java_running_jenkins
and not jenkins_script_sh
and not bundle_running_ruby
output: >
Shell spawned in a container other than entrypoint (user=%user.name %container.info image=%container.image
shell=%proc.name pcmdline=%proc.pcmdline cmdline=%proc.cmdline parent=%proc.pname gparent=%proc.aname[2] ggparent=%proc.aname[3])
priority: DEBUG
output: "Shell spawned in a container other than entrypoint (user=%user.name %container.info shell=%proc.name parent=%proc.pname cmdline=%proc.cmdline)"
priority: NOTICE
tags: [container, shell]
# sockfamily ip is to exclude certain processes (like 'groups') that communicate on unix-domain sockets
# systemd can listen on ports to launch things like sshd on demand
- rule: System procs network activity
desc: any network activity performed by system binaries that are not expected to send or receive any network traffic
condition: (fd.sockfamily = ip and system_procs) and (inbound or outbound) and not proc.name=systemd
output: >
Known system binary sent/received network traffic
(user=%user.name command=%proc.cmdline connection=%fd.name)
condition: (fd.sockfamily = ip and system_procs) and (inbound or outbound)
output: "Known system binary sent/received network traffic (user=%user.name command=%proc.cmdline connection=%fd.name)"
priority: NOTICE
tags: [network]
@@ -878,68 +470,25 @@
# output: "sshd sent error message to syslog (error=%evt.buffer)"
# priority: WARNING
- macro: somebody_becoming_themself
condition: ((user.name=nobody and evt.arg.uid=nobody) or
(user.name=www-data and evt.arg.uid=www-data) or
(user.name=_apt and evt.arg.uid=_apt) or
(user.name=postfix and evt.arg.uid=postfix) or
(user.name=pki-agent and evt.arg.uid=pki-agent) or
(user.name=pki-acme and evt.arg.uid=pki-acme) or
(user.name=nfsnobody and evt.arg.uid=nfsnobody))
# In containers, the user name might be for a uid that exists in the
# container but not on the host. (See
# https://github.com/draios/sysdig/issues/954). So in that case, allow
# a setuid.
- macro: unknown_user_in_container
condition: (user.name="<NA>" and container)
# sshd, mail programs attempt to setuid to root even when running as non-root. Excluding here to avoid meaningless FPs
- rule: Non sudo setuid
desc: >
an attempt to change users by calling setuid. sudo/su are excluded. users "root" and "nobody"
suing to itself are also excluded, as setuid calls typically involve dropping privileges.
condition: >
evt.type=setuid and evt.dir=>
and not unknown_user_in_container
and not user.name=root and not somebody_becoming_themself
and not proc.name in (known_setuid_binaries, userexec_binaries, mail_binaries, docker_binaries, nomachine_binaries)
and not java_running_sdjagent
output: >
Unexpected setuid call by non-sudo, non-root program (user=%user.name parent=%proc.pname
command=%proc.cmdline uid=%evt.arg.uid)
desc: an attempt to change users by calling setuid. sudo/su are excluded. user "root" is also excluded, as setuid calls typically involve dropping privileges.
condition: evt.type=setuid and evt.dir=> and not user.name=root and not proc.name in (userexec_binaries, mail_binaries, sshd, dbus-daemon-lau, ping, ping6, critical-stack-)
output: "Unexpected setuid call by non-sudo, non-root program (user=%user.name parent=%proc.pname command=%proc.cmdline uid=%evt.arg.uid)"
priority: NOTICE
tags: [users]
- rule: User mgmt binaries
desc: >
activity by any programs that can manage users, passwords, or permissions. sudo and su are excluded.
Activity in containers is also excluded--some containers create custom users on top
of a base linux distribution at startup.
Some innocuous commandlines that don't actually change anything are excluded.
condition: >
spawned_process and proc.name in (user_mgmt_binaries) and
not proc.name in (su, sudo, lastlog) and not container and
not proc.pname in (cron_binaries, systemd, run-parts) and
not proc.cmdline startswith "passwd -S" and
not proc.cmdline startswith "useradd -D" and
not proc.cmdline startswith "systemd --version" and
not run_by_qualys
output: >
User management binary command run outside of container
(user=%user.name command=%proc.cmdline parent=%proc.pname gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4])
desc: activity by any programs that can manage users, passwords, or permissions. sudo and su are excluded. Activity in containers is also excluded--some containers create custom users on top of a base linux distribution at startup.
condition: spawned_process and proc.name in (user_mgmt_binaries) and not proc.name in (su, sudo) and not container and not proc.pname in (cron_binaries, systemd, run-parts)
output: "User management binary command run outside of container (user=%user.name command=%proc.cmdline parent=%proc.pname)"
priority: NOTICE
tags: [host, users]
- list: allowed_dev_files
items: [
/dev/null, /dev/stdin, /dev/stdout, /dev/stderr,
/dev/random, /dev/urandom, /dev/console, /dev/kmsg
]
items: [/dev/null, /dev/stdin, /dev/stdout, /dev/stderr, /dev/random, /dev/urandom, /dev/console]
# (we may need to add additional checks against false positives, see:
# https://bugs.launchpad.net/ubuntu/+source/rkhunter/+bug/86153)
# (we may need to add additional checks against false positives, see: https://bugs.launchpad.net/ubuntu/+source/rkhunter/+bug/86153)
- rule: Create files below dev
desc: creating any files below /dev other than known programs that manage devices. Some rootkits hide files in /dev.
condition: >
@@ -970,9 +519,7 @@
- rule: Installer bash non https connection
desc: an attempt by a program in a pipe installer session to make an outgoing connection on a non-http(s) port
condition: proc.sname=fbash and outbound and not fd.sport in (80, 443, 53)
output: >
Outbound connection on non-http(s) port by a process in a fbash session
(command=%proc.cmdline connection=%fd.name)
output: "Outbound connection on non-http(s) port by a process in a fbash session (command=%proc.cmdline connection=%fd.name)"
priority: NOTICE
tags: [network]
@@ -1066,10 +613,7 @@
- macro: cassandra_jmx_port
condition: fd.sport=7199
- macro: cassandra_port
condition: >
cassandra_thrift_client_port or
cassandra_cql_port or cassandra_cluster_port or
cassandra_ssl_cluster_port or cassandra_jmx_port
condition: cassandra_thrift_client_port or cassandra_cql_port or cassandra_cluster_port or cassandra_ssl_cluster_port or cassandra_jmx_port
# - rule: Cassandra unexpected network inbound traffic
# desc: inbound network traffic to cassandra on a port other than the standard ports
@@ -1155,9 +699,7 @@
# - rule: Mongodb unexpected network inbound traffic
# desc: inbound network traffic to mongodb on a port other than the standard ports
# condition: >
# user.name = mongodb and inbound and not (mongodb_server_port or
# mongodb_shardserver_port or mongodb_configserver_port or mongodb_webserver_port)
# condition: user.name = mongodb and inbound and not (mongodb_server_port or mongodb_shardserver_port or mongodb_configserver_port or mongodb_webserver_port)
# output: "Inbound network traffic to MongoDB on unexpected port (connection=%fd.name)"
# priority: WARNING

View File

@@ -7,8 +7,6 @@ file(COPY "${PROJECT_SOURCE_DIR}/scripts/debian/falco"
file(COPY "${PROJECT_SOURCE_DIR}/scripts/rpm/falco"
DESTINATION "${PROJECT_BINARY_DIR}/scripts/rpm")
if(CMAKE_SYSTEM_NAME MATCHES "Linux")
install(PROGRAMS ${SYSDIG_DIR}/scripts/sysdig-probe-loader
DESTINATION ${FALCO_BIN_DIR}
RENAME falco-probe-loader)
endif()
install(PROGRAMS ${SYSDIG_DIR}/scripts/sysdig-probe-loader
DESTINATION bin
RENAME falco-probe-loader)

View File

@@ -30,7 +30,6 @@ class FalcoTest(Test):
self.trace_file = os.path.join(self.basedir, self.trace_file)
self.json_output = self.params.get('json_output', '*', default=False)
self.priority = self.params.get('priority', '*', default='debug')
self.rules_file = self.params.get('rules_file', '*', default=os.path.join(self.basedir, '../rules/falco_rules.yaml'))
if not isinstance(self.rules_file, list):
@@ -296,7 +295,7 @@ class FalcoTest(Test):
res = process.run("docker rm falco-test")
elif self.package.endswith(".deb"):
cmdline = "dpkg --purge falco"
cmdline = "dpkg -r falco"
self.log.debug("Uninstalling debian package via \"{}\"".format(cmdline))
res = process.run(cmdline, timeout=120, sudo=True)
@@ -348,8 +347,8 @@ class FalcoTest(Test):
trace_arg = "-e {}".format(self.trace_file)
# Run falco
cmd = '{} {} {} -c {} {} -o json_output={} -o priority={} -v'.format(
self.falco_binary_path, self.rules_args, self.disabled_args, self.conf_file, trace_arg, self.json_output, self.priority)
cmd = '{} {} {} -c {} {} -o json_output={} -v'.format(
self.falco_binary_path, self.rules_args, self.disabled_args, self.conf_file, trace_arg, self.json_output)
for tag in self.disable_tags:
cmd += ' -T {}'.format(tag)

View File

@@ -129,21 +129,6 @@ trace_files: !mux
- rules/double_rule.yaml
trace_file: trace_files/cat_write.scap
multiple_rules_suppress_info:
detect: True
detect_level:
- WARNING
- ERROR
priority: WARNING
detect_counts:
- open_from_cat: 8
- exec_from_cat: 1
- access_from_cat: 0
rules_file:
- rules/single_rule.yaml
- rules/double_rule.yaml
trace_file: trace_files/cat_write.scap
multiple_rules_overriding:
detect: False
rules_file:
@@ -172,68 +157,6 @@ trace_files: !mux
- rules/override_nested_list.yaml
trace_file: trace_files/cat_write.scap
list_substring:
detect: False
rules_file:
- rules/list_substring.yaml
trace_file: trace_files/cat_write.scap
list_sub_front:
detect: True
detect_level: WARNING
rules_file:
- rules/list_sub_front.yaml
trace_file: trace_files/cat_write.scap
list_sub_mid:
detect: True
detect_level: WARNING
rules_file:
- rules/list_sub_mid.yaml
trace_file: trace_files/cat_write.scap
list_sub_end:
detect: True
detect_level: WARNING
rules_file:
- rules/list_sub_end.yaml
trace_file: trace_files/cat_write.scap
list_sub_bare:
detect: True
detect_level: WARNING
rules_file:
- rules/list_sub_bare.yaml
trace_file: trace_files/cat_write.scap
list_sub_whitespace:
detect: True
detect_level: WARNING
rules_file:
- rules/list_sub_whitespace.yaml
trace_file: trace_files/cat_write.scap
list_order:
detect: True
detect_level: WARNING
rules_file:
- rules/list_order.yaml
trace_file: trace_files/cat_write.scap
macro_order:
detect: True
detect_level: WARNING
rules_file:
- rules/macro_order.yaml
trace_file: trace_files/cat_write.scap
rule_order:
detect: True
detect_level: WARNING
rules_file:
- rules/rule_order.yaml
trace_file: trace_files/cat_write.scap
invalid_rule_output:
exit_status: 1
stderr_contains: "Runtime error: Error loading rules:.* Invalid output format 'An open was seen %not_a_real_field': 'invalid formatting token not_a_real_field'. Exiting."
@@ -594,64 +517,3 @@ trace_files: !mux
- open_11: 1
- open_12: 0
- open_13: 0
list_append_failure:
exit_status: 1
stderr_contains: "List my_list has 'append' key but no list by that name already exists. Exiting"
rules_file:
- rules/list_append_failure.yaml
trace_file: trace_files/cat_write.scap
list_append:
detect: True
detect_level: WARNING
rules_file:
- rules/list_append.yaml
trace_file: trace_files/cat_write.scap
list_append_false:
detect: False
rules_file:
- rules/list_append_false.yaml
trace_file: trace_files/cat_write.scap
macro_append_failure:
exit_status: 1
stderr_contains: "Macro my_macro has 'append' key but no macro by that name already exists. Exiting"
rules_file:
- rules/macro_append_failure.yaml
trace_file: trace_files/cat_write.scap
macro_append:
detect: True
detect_level: WARNING
rules_file:
- rules/macro_append.yaml
trace_file: trace_files/cat_write.scap
macro_append_false:
detect: False
rules_file:
- rules/macro_append_false.yaml
trace_file: trace_files/cat_write.scap
rule_append_failure:
exit_status: 1
stderr_contains: "Rule my_rule has 'append' key but no rule by that name already exists. Exiting"
rules_file:
- rules/rule_append_failure.yaml
trace_file: trace_files/cat_write.scap
rule_append:
detect: True
detect_level: WARNING
rules_file:
- rules/rule_append.yaml
trace_file: trace_files/cat_write.scap
rule_append_false:
detect: False
rules_file:
- rules/rule_append_false.yaml
trace_file: trace_files/cat_write.scap

View File

@@ -17,14 +17,14 @@ traces: !mux
detect: True
detect_level: INFO
detect_counts:
- "Launch Privileged Container": 1
- "File Open by Privileged Container": 19
container-sensitive-mount:
trace_file: traces-positive/container-sensitive-mount.scap
detect: True
detect_level: INFO
detect_counts:
- "Launch Sensitive Mount Container": 1
- "Sensitive Mount by Container": 19
create-files-below-dev:
trace_file: traces-positive/create-files-below-dev.scap
@@ -149,7 +149,7 @@ traces: !mux
shell-in-container:
trace_file: traces-positive/shell-in-container.scap
detect: True
detect_level: DEBUG
detect_level: NOTICE
detect_counts:
- "Run shell in container": 1

View File

@@ -1,12 +0,0 @@
- list: my_list
items: [not-cat]
- list: my_list
append: true
items: [cat]
- rule: Open From Cat
desc: A process named cat does an open
condition: evt.type=open and proc.name in (my_list)
output: "An open was seen (command=%proc.cmdline)"
priority: WARNING

View File

@@ -1,3 +0,0 @@
- list: my_list
items: [not-cat]
append: true

View File

@@ -1,12 +0,0 @@
- list: my_list
items: [cat]
- list: my_list
append: false
items: [not-cat]
- rule: Open From Cat
desc: A process named cat does an open
condition: evt.type=open and proc.name in (my_list)
output: "An open was seen (command=%proc.cmdline)"
priority: WARNING

View File

@@ -1,14 +0,0 @@
- list: cat_binaries
items: [not_cat]
- list: cat_binaries
items: [cat]
- macro: is_cat
condition: proc.name in (cat_binaries)
- 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,11 +0,0 @@
- list: cat_binaries
items: [cat]
- macro: is_cat
condition: proc.name=cat_binaries
- 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,11 +0,0 @@
- list: cat_binaries
items: [cat]
- macro: is_cat
condition: proc.name in (ls, cat_binaries)
- 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,11 +0,0 @@
- list: cat_binaries
items: [cat]
- macro: is_cat
condition: proc.name in (cat_binaries, ps)
- 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,11 +0,0 @@
- list: cat_binaries
items: [cat]
- macro: is_cat
condition: proc.name in (ls, cat_binaries, ps)
- 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,11 +0,0 @@
- list: cat_binaries
items: [cat]
- macro: is_cat
condition: proc.name= cat_binaries or proc.name=nopey
- 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,8 +0,0 @@
- list: my_list
items: ['"one string"']
- rule: my_rule
desc: my description
condition: evt.type=open and fd.name in (file_my_list)
output: my output
priority: INFO

View File

@@ -1,12 +0,0 @@
- macro: my_macro
condition: proc.name=not-cat
- macro: my_macro
append: true
condition: or proc.name=cat
- rule: Open From Cat
desc: A process named cat does an open
condition: evt.type=open and my_macro
output: "An open was seen (command=%proc.cmdline)"
priority: WARNING

View File

@@ -1,3 +0,0 @@
- macro: my_macro
condition: proc.name=not-cat
append: true

View File

@@ -1,12 +0,0 @@
- macro: my_macro
condition: proc.name=cat
- macro: my_macro
append: false
condition: proc.name=not-cat
- rule: Open From Cat
desc: A process named cat does an open
condition: evt.type=open and my_macro
output: "An open was seen (command=%proc.cmdline)"
priority: WARNING

View File

@@ -1,14 +0,0 @@
- list: cat_binaries
items: [cat]
- macro: is_cat
condition: proc.name in (not_cat)
- macro: is_cat
condition: proc.name in (cat_binaries)
- 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,9 +0,0 @@
- rule: my_rule
desc: A process named cat does an open
condition: evt.type=open and fd.name=not-a-real-file
output: "An open of /dev/null was seen (command=%proc.cmdline)"
priority: WARNING
- rule: my_rule
append: true
condition: or fd.name=/dev/null

View File

@@ -1,3 +0,0 @@
- rule: my_rule
condition: evt.type=open
append: true

View File

@@ -1,9 +0,0 @@
- rule: my_rule
desc: A process named cat does an open
condition: evt.type=open and fd.name=/dev/null
output: "An open of /dev/null was seen (command=%proc.cmdline)"
priority: WARNING
- rule: my_rule
append: true
condition: and fd.name=not-a-real-file

View File

@@ -1,17 +0,0 @@
- list: cat_binaries
items: [cat]
- macro: is_cat
condition: proc.name in (cat_binaries)
- rule: open_from_cat
desc: A process named cat does an open
condition: evt.type=open and proc.name=not_cat
output: "An open was seen (command=%proc.cmdline)"
priority: WARNING
- 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

@@ -27,3 +27,5 @@ install(DIRECTORY lua
DESTINATION "${FALCO_SHARE_DIR}"
FILES_MATCHING PATTERN *.lua)
endif()
add_subdirectory("${PROJECT_SOURCE_DIR}/../falco/rules" "${PROJECT_BINARY_DIR}/rules")

View File

@@ -18,5 +18,5 @@ along with falco. If not, see <http://www.gnu.org/licenses/>.
#pragma once
#define FALCO_ENGINE_LUA_DIR "${FALCO_ABSOLUTE_SHARE_DIR}/lua/"
#define FALCO_ENGINE_LUA_DIR "${FALCO_SHARE_DIR}/lua/"
#define FALCO_ENGINE_SOURCE_LUA_DIR "${PROJECT_SOURCE_DIR}/../falco/userspace/engine/lua/"

View File

@@ -21,16 +21,6 @@ along with falco. If not, see <http://www.gnu.org/licenses/>.
#include "config_falco_engine.h"
#include "falco_common.h"
std::vector<std::string> falco_common::priority_names = {
"Emergency",
"Alert",
"Critical",
"Error",
"Warning",
"Notice",
"Informational",
"Debug"};
falco_common::falco_common()
{
m_ls = lua_open();

View File

@@ -74,22 +74,6 @@ public:
void set_inspector(sinsp *inspector);
// Priority levels, as a vector of strings
static std::vector<std::string> priority_names;
// Same as numbers/indices into the above vector
enum priority_type
{
PRIORITY_EMERGENCY = 0,
PRIORITY_ALERT = 1,
PRIORITY_CRITICAL = 2,
PRIORITY_ERROR = 3,
PRIORITY_WARNING = 4,
PRIORITY_NOTICE = 5,
PRIORITY_INFORMATIONAL = 6,
PRIORITY_DEBUG = 7
};
protected:
lua_State *m_ls;

View File

@@ -41,7 +41,6 @@ using namespace std;
falco_engine::falco_engine(bool seed_rng)
: m_rules(NULL), m_next_ruleset_id(0),
m_min_priority(falco_common::PRIORITY_DEBUG),
m_sampling_ratio(1), m_sampling_multiplier(0),
m_replace_container_info(false)
{
@@ -90,7 +89,7 @@ void falco_engine::load_rules(const string &rules_content, bool verbose, bool al
bool json_output = false;
falco_formats::init(m_inspector, m_ls, json_output);
m_rules->load_rules(rules_content, verbose, all_events, m_extra, m_replace_container_info, m_min_priority);
m_rules->load_rules(rules_content, verbose, all_events, m_extra, m_replace_container_info);
}
void falco_engine::load_rules_file(const string &rules_filename, bool verbose, bool all_events)
@@ -135,11 +134,6 @@ void falco_engine::enable_rule_by_tag(const set<string> &tags, bool enabled)
enable_rule_by_tag(tags, enabled, m_default_ruleset);
}
void falco_engine::set_min_priority(falco_common::priority_type priority)
{
m_min_priority = priority;
}
uint16_t falco_engine::find_ruleset_id(const std::string &ruleset)
{
auto it = m_known_rulesets.lower_bound(ruleset);
@@ -184,7 +178,7 @@ unique_ptr<falco_engine::rule_result> falco_engine::process_event(sinsp_evt *ev,
res->evt = ev;
const char *p = lua_tostring(m_ls, -3);
res->rule = p;
res->priority_num = (falco_common::priority_type) lua_tonumber(m_ls, -2);
res->priority = lua_tostring(m_ls, -2);
res->format = lua_tostring(m_ls, -1);
lua_pop(m_ls, 3);
}

View File

@@ -67,13 +67,10 @@ public:
// Wrapper that assumes the default ruleset
void enable_rule_by_tag(const std::set<std::string> &tags, bool enabled);
// Only load rules having this priority or more severe.
void set_min_priority(falco_common::priority_type priority);
struct rule_result {
sinsp_evt *evt;
std::string rule;
falco_common::priority_type priority_num;
std::string priority;
std::string format;
};
@@ -161,7 +158,6 @@ private:
uint16_t m_next_ruleset_id;
std::map<string, uint16_t> m_known_rulesets;
std::unique_ptr<sinsp_evttype_filter> m_evttype_filter;
falco_common::priority_type m_min_priority;
//
// Here's how the sampling ratio and multiplier influence

View File

@@ -92,7 +92,6 @@ int falco_formats::free_formatters(lua_State *ls)
int falco_formats::format_event (lua_State *ls)
{
string line;
string json_line;
if (!lua_isstring(ls, -1) ||
!lua_isstring(ls, -2) ||
@@ -110,20 +109,6 @@ int falco_formats::format_event (lua_State *ls)
try {
s_formatters->tostring(evt, sformat, &line);
if(s_json_output)
{
s_inspector->set_buffer_format(sinsp_evt::PF_JSON);
s_formatters->tostring(evt, sformat, &json_line);
// The formatted string might have a leading newline. If it does, remove it.
if (json_line[0] == '\n')
{
json_line.erase(0, 1);
}
s_inspector->set_buffer_format(sinsp_evt::PF_NORMAL);
}
}
catch (sinsp_exception& e)
{
@@ -132,15 +117,13 @@ int falco_formats::format_event (lua_State *ls)
lua_error(ls);
}
// For JSON output, the formatter returned a json-as-text
// object containing all the fields in the original format
// message as well as the event time in ns. Use this to build
// a more detailed object containing the event time, rule,
// severity, full output, and fields.
// For JSON output, the formatter returned just the output
// string containing the format text and values. Use this to
// build a more detailed object containing the event time,
// rule, severity, full output, and fields.
if (s_json_output) {
Json::Value event;
Json::FastWriter writer;
string full_line;
// Convert the time-as-nanoseconds to a more json-friendly ISO8601.
time_t evttime = evt->get_ts()/1000000000;
@@ -155,26 +138,16 @@ int falco_formats::format_event (lua_State *ls)
event["time"] = iso8601evttime;
event["rule"] = rule;
event["priority"] = level;
// This is the filled-in output line.
event["output"] = line;
full_line = writer.write(event);
line = writer.write(event);
// Json::FastWriter may add a trailing newline. If it
// does, remove it.
if (full_line[full_line.length()-1] == '\n')
if (line[line.length()-1] == '\n')
{
full_line.resize(full_line.length()-1);
line.resize(line.length()-1);
}
// Cheat-graft the output from the formatter into this
// string. Avoids an unnecessary json parse just to
// merge the formatted fields at the object level.
full_line.pop_back();
full_line.append(", \"output_fields\": ");
full_line.append(json_line);
full_line.append("}");
line = full_line;
}
lua_pushstring(ls, line.c_str());

View File

@@ -20,7 +20,6 @@ local compiler = {}
compiler.verbose = false
compiler.all_events = false
compiler.trim = parser.trim
function compiler.set_verbose(verbose)
compiler.verbose = verbose
@@ -59,16 +58,15 @@ end
definition uses another macro).
--]]
function copy_ast_obj(obj)
if type(obj) ~= 'table' then return obj end
local res = {}
for k, v in pairs(obj) do res[copy_ast_obj(k)] = copy_ast_obj(v) end
return res
end
function expand_macros(ast, defs, changed)
function copy(obj)
if type(obj) ~= 'table' then return obj end
local res = {}
for k, v in pairs(obj) do res[copy(k)] = copy(v) end
return res
end
if (ast.type == "Rule") then
return expand_macros(ast.filter, defs, changed)
elseif ast.type == "Filter" then
@@ -76,7 +74,7 @@ function expand_macros(ast, defs, changed)
if (defs[ast.value.value] == nil) then
error("Undefined macro '".. ast.value.value .. "' used in filter.")
end
ast.value = copy_ast_obj(defs[ast.value.value])
ast.value = copy(defs[ast.value.value])
changed = true
return changed
end
@@ -88,7 +86,7 @@ function expand_macros(ast, defs, changed)
if (defs[ast.left.value] == nil) then
error("Undefined macro '".. ast.left.value .. "' used in filter.")
end
ast.left = copy_ast_obj(defs[ast.left.value])
ast.left = copy(defs[ast.left.value])
changed = true
end
@@ -96,7 +94,7 @@ function expand_macros(ast, defs, changed)
if (defs[ast.right.value] == nil) then
error("Undefined macro ".. ast.right.value .. " used in filter.")
end
ast.right = copy_ast_obj(defs[ast.right.value])
ast.right = copy(defs[ast.right.value])
changed = true
end
@@ -109,7 +107,7 @@ function expand_macros(ast, defs, changed)
if (defs[ast.argument.value] == nil) then
error("Undefined macro ".. ast.argument.value .. " used in filter.")
end
ast.argument = copy_ast_obj(defs[ast.argument.value])
ast.argument = copy(defs[ast.argument.value])
changed = true
end
return expand_macros(ast.argument, defs, changed)
@@ -283,7 +281,7 @@ function get_evttypes(name, ast, source)
return evttypes
end
function compiler.compile_macro(line, macro_defs, list_defs)
function compiler.compile_macro(line, list_defs)
for name, items in pairs(list_defs) do
line = string.gsub(line, name, table.concat(items, ", "))
@@ -302,21 +300,6 @@ function compiler.compile_macro(line, macro_defs, list_defs)
check_for_ignored_syscalls_events(ast, 'macro', line)
end
-- Simply as a validation step, try to expand all macros in this
-- macro's condition. This changes the ast, so we make a copy
-- first.
local ast_copy = copy_ast_obj(ast)
if (ast.type == "Rule") then
-- Line is a filter, so expand macro references
repeat
expanded = expand_macros(ast_copy, macro_defs, false)
until expanded == false
else
error("Unexpected top-level AST type: "..ast.type)
end
return ast
end
@@ -326,12 +309,7 @@ end
function compiler.compile_filter(name, source, macro_defs, list_defs)
for name, items in pairs(list_defs) do
local begin_name_pat = "^("..name..")([%s(),=])"
local mid_name_pat = "([%s(),=])("..name..")([%s(),=])"
local end_name_pat = "([%s(),=])("..name..")$"
source = string.gsub(source, begin_name_pat, table.concat(items, ", ").."%2")
source = string.gsub(source, mid_name_pat, "%1"..table.concat(items, ", ").."%3")
source = string.gsub(source, end_name_pat, "%1"..table.concat(items, ", "))
source = string.gsub(source, name, table.concat(items, ", "))
end
local ast, error_msg = parser.parse_filter(source)

View File

@@ -127,11 +127,10 @@ function trim(s)
if (type(s) ~= "string") then return s end
return (s:gsub("^%s*(.-)%s*$", "%1"))
end
parser.trim = trim
local function terminal (tag)
-- Rather than trim the whitespace in this way, it would be nicer to exclude it from the capture...
return token(V(tag), tag) / function (tok) val = tok; if tag ~= "String" then val = trim(tok) end; return { type = tag, value = val} end
return token(V(tag), tag) / function (tok) return { type = tag, value = trim(tok)} end
end
local function unaryboolop (op, e)
@@ -238,7 +237,7 @@ local G = {
Identifier = V"idStart" * V"idRest"^0;
Macro = V"idStart" * V"idRest"^0 * -P".";
Int = digit^1;
PathString = (alnum + S'.-_/*?')^1;
PathString = (alnum + S'-_/*?')^1;
Index = V"Int" + V"PathString";
FieldName = V"Identifier" * (P"." + V"Identifier")^1 * (P"[" * V"Index" * P"]")^-1;
Name = C(V"Identifier") * -V"idRest";

View File

@@ -58,17 +58,6 @@ function map(f, arr)
return res
end
priorities = {"Emergency", "Alert", "Critical", "Error", "Warning", "Notice", "Informational", "Debug"}
local function priority_num_for(s)
s = string.lower(s)
for i,v in ipairs(priorities) do
if (string.find(string.lower(v), "^"..s)) then
return i - 1 -- (numbers start at 0, lua indices start at 1)
end
end
error("Invalid priority level: "..s)
end
--[[
Take a filter AST and set it up in the libsinsp runtime, using the filter API.
@@ -182,7 +171,7 @@ function table.tostring( tbl )
end
function load_rules(rules_content, rules_mgr, verbose, all_events, extra, replace_container_info, min_priority)
function load_rules(rules_content, rules_mgr, verbose, all_events, extra, replace_container_info)
compiler.set_verbose(verbose)
compiler.set_all_events(all_events)
@@ -219,23 +208,7 @@ function load_rules(rules_content, rules_mgr, verbose, all_events, extra, replac
end
end
-- Possibly append to the condition field of an existing macro
append = false
if v['append'] then
append = v['append']
end
if append then
if state.macros_by_name[v['macro']] == nil then
error ("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']
else
state.macros_by_name[v['macro']] = v
end
state.macros_by_name[v['macro']] = v
elseif (v['list']) then
@@ -249,24 +222,7 @@ function load_rules(rules_content, rules_mgr, verbose, all_events, extra, replac
end
end
-- Possibly append to an existing list
append = false
if v['append'] then
append = v['append']
end
if append then
if state.lists_by_name[v['list']] == nil then
error ("List " ..v['list'].. " has 'append' key but no list by that name already exists")
end
for i, elem in ipairs(v['items']) do
table.insert(state.lists_by_name[v['list']]['items'], elem)
end
else
state.lists_by_name[v['list']] = v
end
state.lists_by_name[v['list']] = v
elseif (v['rule']) then
@@ -274,54 +230,21 @@ function load_rules(rules_content, rules_mgr, verbose, all_events, extra, replac
error ("Missing name in rule")
end
-- Possibly append to the condition field of an existing rule
append = false
if v['append'] then
append = v['append']
end
if append then
-- For append rules, all you need is the condition
for i, field in ipairs({'condition'}) do
if (v[field] == nil) then
error ("Missing "..field.." in rule with name "..v['rule'])
end
end
if state.rules_by_name[v['rule']] == nil then
error ("Rule " ..v['rule'].. " has 'append' key but no rule by that name already exists")
end
state.rules_by_name[v['rule']]['condition'] = state.rules_by_name[v['rule']]['condition'] .. " " .. v['condition']
else
for i, field in ipairs({'condition', 'output', 'desc', 'priority'}) do
if (v[field] == nil) then
error ("Missing "..field.." in rule with name "..v['rule'])
end
end
-- Convert the priority-as-string to a priority-as-number now
v['priority_num'] = priority_num_for(v['priority'])
if v['priority_num'] <= min_priority then
-- Note that we can overwrite rules, but the rules are still
-- loaded in the order in which they first appeared,
-- potentially across multiple files.
if state.rules_by_name[v['rule']] == nil then
state.ordered_rule_names[#state.ordered_rule_names+1] = v['rule']
end
-- The output field might be a folded-style, which adds a
-- newline to the end. Remove any trailing newlines.
v['output'] = compiler.trim(v['output'])
state.rules_by_name[v['rule']] = v
for i, field in ipairs({'condition', 'output', 'desc', 'priority'}) do
if (v[field] == nil) then
error ("Missing "..field.." in rule with name "..v['rule'])
end
end
-- Note that we can overwrite rules, but the rules are still
-- loaded in the order in which they first appeared,
-- potentially across multiple files.
if state.rules_by_name[v['rule']] == nil then
state.ordered_rule_names[#state.ordered_rule_names+1] = v['rule']
end
state.rules_by_name[v['rule']] = v
else
error ("Unknown rule object: "..table.tostring(v))
end
@@ -360,7 +283,7 @@ function load_rules(rules_content, rules_mgr, verbose, all_events, extra, replac
local v = state.macros_by_name[name]
local ast = compiler.compile_macro(v['condition'], state.macros, state.lists)
local ast = compiler.compile_macro(v['condition'], state.lists)
state.macros[v['macro']] = ast.filter.value
end
@@ -520,7 +443,7 @@ function on_event(evt_, rule_id)
-- Prefix output with '*' so formatting is permissive
output = "*"..rule.output
return rule.rule, rule.priority_num, output
return rule.rule, rule.priority, output
end
function print_stats()

View File

@@ -145,8 +145,7 @@ void falco_rules::enable_rule(string &rule, bool enabled)
void falco_rules::load_rules(const string &rules_content,
bool verbose, bool all_events,
string &extra, bool replace_container_info,
falco_common::priority_type min_priority)
string &extra, bool replace_container_info)
{
lua_getglobal(m_ls, m_lua_load_rules.c_str());
if(lua_isfunction(m_ls, -1))
@@ -222,8 +221,7 @@ void falco_rules::load_rules(const string &rules_content,
lua_pushboolean(m_ls, (all_events ? 1 : 0));
lua_pushstring(m_ls, extra.c_str());
lua_pushboolean(m_ls, (replace_container_info ? 1 : 0));
lua_pushnumber(m_ls, min_priority);
if(lua_pcall(m_ls, 7, 0, 0) != 0)
if(lua_pcall(m_ls, 6, 0, 0) != 0)
{
const char* lerr = lua_tostring(m_ls, -1);
string err = "Error loading rules:" + string(lerr);

View File

@@ -24,8 +24,6 @@ along with falco. If not, see <http://www.gnu.org/licenses/>.
#include "lua_parser.h"
#include "falco_common.h"
class falco_engine;
class falco_rules
@@ -34,8 +32,7 @@ class falco_rules
falco_rules(sinsp* inspector, falco_engine *engine, lua_State *ls);
~falco_rules();
void load_rules(const string &rules_content, bool verbose, bool all_events,
std::string &extra, bool replace_container_info,
falco_common::priority_type min_priority);
std::string &extra, bool replace_container_info);
void describe_rule(string *rule);
static void init(lua_State *ls);

View File

@@ -19,7 +19,7 @@ target_link_libraries(falco
configure_file(config_falco.h.in config_falco.h)
install(TARGETS falco DESTINATION ${FALCO_BIN_DIR})
install(TARGETS falco DESTINATION bin)
install(DIRECTORY lua
DESTINATION ${FALCO_SHARE_DIR}
DESTINATION share/falco
FILES_MATCHING PATTERN *.lua)

View File

@@ -5,7 +5,7 @@
#define FALCO_LUA_DIR "${CMAKE_INSTALL_PREFIX}/${FALCO_SHARE_DIR}/lua/"
#define FALCO_SOURCE_DIR "${PROJECT_SOURCE_DIR}"
#define FALCO_SOURCE_CONF_FILE "${PROJECT_SOURCE_DIR}/falco.yaml"
#define FALCO_INSTALL_CONF_FILE "/etc/falco/falco.yaml"
#define FALCO_INSTALL_CONF_FILE "/etc/falco.yaml"
#define FALCO_SOURCE_LUA_DIR "${PROJECT_SOURCE_DIR}/userspace/falco/lua/"
#define PROBE_NAME "${PROBE_NAME}"

View File

@@ -22,8 +22,7 @@ along with falco. If not, see <http://www.gnu.org/licenses/>.
using namespace std;
falco_configuration::falco_configuration()
: m_buffered_outputs(true),
m_config(NULL)
: m_config(NULL)
{
}
@@ -52,37 +51,20 @@ void falco_configuration::init(string conf_filename, list<string> &cmdline_optio
init_cmdline_options(cmdline_options);
list<string> rules_files;
m_config->get_sequence<list<string>>(rules_files, string("rules_file"));
for(auto &file : rules_files)
{
// Here, we only include files that exist
struct stat buffer;
if(stat(file.c_str(), &buffer) == 0)
{
m_rules_filenames.push_back(file);
}
}
m_rules_filenames.push_back(m_config->get_scalar<string>("rules_file", "/etc/falco_rules.yaml"));
m_json_output = m_config->get_scalar<bool>("json_output", false);
falco_outputs::output_config file_output;
file_output.name = "file";
if (m_config->get_scalar<bool>("file_output", "enabled", false))
{
string filename, keep_alive;
string filename;
filename = m_config->get_scalar<string>("file_output", "filename", "");
if (filename == string(""))
{
throw invalid_argument("Error reading config file (" + m_config_file + "): file output enabled but no filename in configuration block");
}
file_output.options["filename"] = filename;
keep_alive = m_config->get_scalar<string>("file_output", "keep_alive", "");
file_output.options["keep_alive"] = keep_alive;
m_outputs.push_back(file_output);
}
@@ -104,17 +86,13 @@ void falco_configuration::init(string conf_filename, list<string> &cmdline_optio
program_output.name = "program";
if (m_config->get_scalar<bool>("program_output", "enabled", false))
{
string program, keep_alive;
string program;
program = m_config->get_scalar<string>("program_output", "program", "");
if (program == string(""))
{
throw sinsp_exception("Error reading config file (" + m_config_file + "): program output enabled but no program in configuration block");
}
program_output.options["program"] = program;
keep_alive = m_config->get_scalar<string>("program_output", "keep_alive", "");
program_output.options["keep_alive"] = keep_alive;
m_outputs.push_back(program_output);
}
@@ -130,21 +108,6 @@ void falco_configuration::init(string conf_filename, list<string> &cmdline_optio
m_notifications_rate = m_config->get_scalar<uint32_t>("outputs", "rate", 1);
m_notifications_max_burst = m_config->get_scalar<uint32_t>("outputs", "max_burst", 1000);
string priority = m_config->get_scalar<string>("priority", "debug");
vector<string>::iterator it;
auto comp = [priority] (string &s) {
return (strcasecmp(s.c_str(), priority.c_str()) == 0);
};
if((it = std::find_if(falco_common::priority_names.begin(), falco_common::priority_names.end(), comp)) == falco_common::priority_names.end())
{
throw invalid_argument("Unknown priority \"" + priority + "\"--must be one of emergency, alert, critical, error, warning, notice, informational, debug");
}
m_min_priority = (falco_common::priority_type) (it - falco_common::priority_names.begin());
m_buffered_outputs = m_config->get_scalar<bool>("buffered_outputs", true);
falco_logger::log_stderr = m_config->get_scalar<bool>("log_stderr", false);
falco_logger::log_syslog = m_config->get_scalar<bool>("log_syslog", true);
}

View File

@@ -18,9 +18,6 @@ along with falco. If not, see <http://www.gnu.org/licenses/>.
#pragma once
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <yaml-cpp/yaml.h>
#include <string>
#include <vector>
@@ -130,27 +127,6 @@ public:
}
}
// called with the last variadic arg (where the sequence is expected to be found)
template <typename T>
void get_sequence(T& ret, const std::string& name)
{
YAML::Node child_node = m_root[name];
if(child_node.IsDefined())
{
if(child_node.IsSequence())
{
for(const YAML::Node& item : child_node)
{
ret.insert(ret.end(), item.as<typename T::value_type>());
}
}
else if(child_node.IsScalar())
{
ret.insert(ret.end(), child_node.as<typename T::value_type>());
}
}
}
private:
YAML::Node m_root;
};
@@ -170,10 +146,6 @@ class falco_configuration
std::vector<falco_outputs::output_config> m_outputs;
uint32_t m_notifications_rate;
uint32_t m_notifications_max_burst;
falco_common::priority_type m_min_priority;
bool m_buffered_outputs;
private:
void init_cmdline_options(std::list<std::string> &cmdline_options);

View File

@@ -107,11 +107,6 @@ static void usage()
" Can not be specified with -t.\n"
" -t <tag> Only run those rules with a tag=<tag>. Can be specified multiple times.\n"
" Can not be specified with -T/-D.\n"
" -U,--unbuffered Turn off output buffering to configured outputs. This causes every\n"
" single line emitted by falco to be flushed, which generates higher CPU\n"
" usage but is useful when piping those outputs into another process\n"
" or into a script.\n"
" -V,--validate <rules_file> Read the contents of the specified rules file and exit\n"
" -v Verbose output.\n"
" --version Print version number.\n"
"\n"
@@ -217,7 +212,7 @@ uint64_t do_inspect(falco_engine *engine,
unique_ptr<falco_engine::rule_result> res = engine->process_event(ev);
if(res)
{
outputs->handle_event(res->evt, res->rule, res->priority_num, res->format);
outputs->handle_event(res->evt, res->rule, res->priority, res->format);
}
num_evts++;
@@ -245,7 +240,6 @@ int falco_init(int argc, char **argv)
string pidfilename = "/var/run/falco.pid";
bool describe_all_rules = false;
string describe_rule = "";
string validate_rules_file = "";
string stats_filename = "";
bool verbose = false;
bool all_events = false;
@@ -262,8 +256,6 @@ int falco_init(int argc, char **argv)
int file_limit = 0;
unsigned long event_limit = 0L;
bool compress = false;
bool buffered_outputs = true;
bool buffered_cmdline = false;
// Used for stats
uint64_t num_evts;
@@ -280,9 +272,7 @@ int falco_init(int argc, char **argv)
{"option", required_argument, 0, 'o'},
{"print", required_argument, 0, 'p' },
{"pidfile", required_argument, 0, 'P' },
{"unbuffered", no_argument, 0, 'U' },
{"version", no_argument, 0, 0 },
{"validate", required_argument, 0, 0 },
{"writefile", required_argument, 0, 'w' },
{0, 0, 0, 0}
@@ -300,7 +290,7 @@ int falco_init(int argc, char **argv)
// Parse the args
//
while((op = getopt_long(argc, argv,
"hc:AdD:e:k:K:Ll:m:M:o:P:p:r:s:T:t:UvV:w:",
"hc:AdD:e:k:K:Ll:m:M:o:P:p:r:s:T:t:vw:",
long_options, &long_index)) != -1)
{
switch(op)
@@ -388,16 +378,9 @@ int falco_init(int argc, char **argv)
case 't':
enabled_rule_tags.insert(optarg);
break;
case 'U':
buffered_outputs = false;
buffered_cmdline = true;
break;
case 'v':
verbose = true;
break;
case 'V':
validate_rules_file = optarg;
break;
case 'w':
outfile = optarg;
break;
@@ -460,14 +443,6 @@ int falco_init(int argc, char **argv)
}
}
if(validate_rules_file != "")
{
falco_logger::log(LOG_INFO, "Validating rules file: " + validate_rules_file + "...\n");
engine->load_rules_file(validate_rules_file, verbose, all_events);
falco_logger::log(LOG_INFO, "Ok\n");
goto exit;
}
falco_configuration config;
if (conf_filename.size())
{
@@ -486,18 +461,6 @@ int falco_init(int argc, char **argv)
config.m_rules_filenames = rules_filenames;
}
engine->set_min_priority(config.m_min_priority);
if(buffered_cmdline)
{
config.m_buffered_outputs = buffered_outputs;
}
if(config.m_rules_filenames.size() == 0)
{
throw std::invalid_argument("You must specify at least one rules file via -r or a rules_file entry in falco.yaml");
}
for (auto filename : config.m_rules_filenames)
{
engine->load_rules_file(filename, verbose, all_events);
@@ -538,9 +501,7 @@ int falco_init(int argc, char **argv)
engine->enable_rule_by_tag(enabled_rule_tags, true);
}
outputs->init(config.m_json_output,
config.m_notifications_rate, config.m_notifications_max_burst,
config.m_buffered_outputs);
outputs->init(config.m_json_output, config.m_notifications_rate, config.m_notifications_max_burst);
if(!all_events)
{

View File

@@ -27,8 +27,7 @@ along with falco. If not, see <http://www.gnu.org/licenses/>.
using namespace std;
falco_outputs::falco_outputs()
: m_initialized(false),
m_buffered(true)
: m_initialized(false)
{
}
@@ -52,7 +51,7 @@ falco_outputs::~falco_outputs()
}
}
void falco_outputs::init(bool json_output, uint32_t rate, uint32_t max_burst, bool buffered)
void falco_outputs::init(bool json_output, uint32_t rate, uint32_t max_burst)
{
// The engine must have been given an inspector by now.
if(! m_inspector)
@@ -71,14 +70,12 @@ void falco_outputs::init(bool json_output, uint32_t rate, uint32_t max_burst, bo
m_notifications_tb.init(rate, max_burst);
m_buffered = buffered;
m_initialized = true;
}
void falco_outputs::add_output(output_config oc)
{
uint8_t nargs = 2;
uint8_t nargs = 1;
lua_getglobal(m_ls, m_lua_add_output.c_str());
if(!lua_isfunction(m_ls, -1))
@@ -86,12 +83,11 @@ void falco_outputs::add_output(output_config oc)
throw falco_exception("No function " + m_lua_add_output + " found. ");
}
lua_pushstring(m_ls, oc.name.c_str());
lua_pushnumber(m_ls, (m_buffered ? 1 : 0));
// If we have options, build up a lua table containing them
if (oc.options.size())
{
nargs = 3;
nargs = 2;
lua_createtable(m_ls, 0, oc.options.size());
for (auto it = oc.options.cbegin(); it != oc.options.cend(); ++it)
@@ -109,7 +105,7 @@ void falco_outputs::add_output(output_config oc)
}
void falco_outputs::handle_event(sinsp_evt *ev, string &rule, falco_common::priority_type priority, string &format)
void falco_outputs::handle_event(sinsp_evt *ev, string &rule, string &priority, string &format)
{
if(!m_notifications_tb.claim())
{
@@ -123,11 +119,10 @@ void falco_outputs::handle_event(sinsp_evt *ev, string &rule, falco_common::prio
{
lua_pushlightuserdata(m_ls, ev);
lua_pushstring(m_ls, rule.c_str());
lua_pushstring(m_ls, falco_common::priority_names[priority].c_str());
lua_pushnumber(m_ls, priority);
lua_pushstring(m_ls, priority.c_str());
lua_pushstring(m_ls, format.c_str());
if(lua_pcall(m_ls, 5, 0, 0) != 0)
if(lua_pcall(m_ls, 4, 0, 0) != 0)
{
const char* lerr = lua_tostring(m_ls, -1);
string err = "Error invoking function output: " + string(lerr);

View File

@@ -41,7 +41,7 @@ public:
std::map<std::string, std::string> options;
};
void init(bool json_output, uint32_t rate, uint32_t max_burst, bool buffered);
void init(bool json_output, uint32_t rate, uint32_t max_burst);
void add_output(output_config oc);
@@ -49,7 +49,7 @@ public:
// ev is an event that has matched some rule. Pass the event
// to all configured outputs.
//
void handle_event(sinsp_evt *ev, std::string &rule, falco_common::priority_type priority, std::string &format);
void handle_event(sinsp_evt *ev, std::string &rule, std::string &priority, std::string &format);
private:
bool m_initialized;
@@ -57,8 +57,6 @@ private:
// Rate limits notifications
token_bucket m_notifications_tb;
bool m_buffered;
std::string m_lua_add_output = "add_output";
std::string m_lua_output_event = "output_event";
std::string m_lua_output_cleanup = "output_cleanup";

View File

@@ -18,25 +18,22 @@
local mod = {}
levels = {"Emergency", "Alert", "Critical", "Error", "Warning", "Notice", "Informational", "Debug"}
mod.levels = levels
local outputs = {}
function mod.stdout(priority, priority_num, buffered, msg)
if buffered == 0 then
io.stdout:setvbuf 'no'
end
function mod.stdout(level, msg)
print (msg)
end
function mod.stdout_cleanup()
io.stdout:flush()
end
function mod.file_validate(options)
if (not type(options.filename) == 'string') then
error("File output needs to be configured with a valid filename")
end
local file, err = io.open(options.filename, "a+")
file, err = io.open(options.filename, "a+")
if file == nil then
error("Error with file output: "..err)
end
@@ -44,100 +41,60 @@ function mod.file_validate(options)
end
function mod.file(priority, priority_num, buffered, msg, options)
if options.keep_alive == "true" then
if file == nil then
file = io.open(options.filename, "a+")
if buffered == 0 then
file:setvbuf 'no'
end
end
else
file = io.open(options.filename, "a+")
end
function mod.file(level, msg, options)
file = io.open(options.filename, "a+")
file:write(msg, "\n")
if options.keep_alive == nil or
options.keep_alive ~= "true" then
file:close()
file = nil
end
file:close()
end
function mod.file_cleanup()
if file ~= nil then
file:flush()
file:close()
file = nil
end
function mod.syslog(level, msg, options)
falco.syslog(level, msg)
end
function mod.syslog(priority, priority_num, buffered, msg, options)
falco.syslog(priority_num, msg)
end
function mod.syslog_cleanup()
end
function mod.program(priority, priority_num, buffered, msg, options)
function mod.program(level, msg, options)
-- XXX Ideally we'd check that the program ran
-- successfully. However, the luajit we're using returns true even
-- when the shell can't run the program.
-- Note: options are all strings
if options.keep_alive == "true" then
if file == nil then
file = io.popen(options.program, "w")
if buffered == 0 then
file:setvbuf 'no'
end
end
else
file = io.popen(options.program, "w")
end
file = io.popen(options.program, "w")
file:write(msg, "\n")
if options.keep_alive == nil or
options.keep_alive ~= "true" then
file:close()
file = nil
end
file:close()
end
function mod.program_cleanup()
if file ~= nil then
file:flush()
file:close()
file = nil
local function level_of(s)
s = string.lower(s)
for i,v in ipairs(levels) do
if (string.find(string.lower(v), "^"..s)) then
return i - 1 -- (syslog levels start at 0, lua indices start at 1)
end
end
error("Invalid severity level: "..s)
end
function output_event(event, rule, priority, priority_num, format)
function output_event(event, rule, priority, format)
local level = level_of(priority)
-- If format starts with a *, remove it, as we're adding our own
-- prefix here.
if format:sub(1,1) == "*" then
format = format:sub(2)
end
format = "*%evt.time: "..priority.." "..format
format = "*%evt.time: "..levels[level+1].." "..format
msg = formats.format_event(event, rule, priority, format)
msg = formats.format_event(event, rule, levels[level+1], format)
for index,o in ipairs(outputs) do
o.output(priority, priority_num, o.buffered, msg, o.config)
o.output(level, msg, o.config)
end
end
function output_cleanup()
formats.free_formatters()
for index,o in ipairs(outputs) do
o.cleanup()
end
end
function add_output(output_name, buffered, config)
function add_output(output_name, config)
if not (type(mod[output_name]) == 'function') then
error("rule_loader.add_output(): invalid output_name: "..output_name)
end
@@ -148,9 +105,7 @@ function add_output(output_name, buffered, config)
mod[output_name.."_validate"](config)
end
table.insert(outputs, {output = mod[output_name],
cleanup = mod[output_name.."_cleanup"],
buffered=buffered, config=config})
table.insert(outputs, {output = mod[output_name], config=config})
end
return mod