mirror of
https://github.com/falcosecurity/falco.git
synced 2026-04-01 09:32:29 +00:00
Compare commits
15 Commits
0.8.1
...
agent/0.69
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
498d083980 | ||
|
|
6fd7f0d628 | ||
|
|
d6fe29b47d | ||
|
|
a71cbcd7ee | ||
|
|
99d6bccc81 | ||
|
|
f92f74eaa8 | ||
|
|
d42d0e2dd1 | ||
|
|
135b4d9975 | ||
|
|
a25166b7ac | ||
|
|
800a3f1ea1 | ||
|
|
31464de885 | ||
|
|
9b308d2793 | ||
|
|
a99f09da96 | ||
|
|
1e0ddba11a | ||
|
|
b6d1101cb6 |
55
CHANGELOG.md
55
CHANGELOG.md
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
@@ -399,9 +399,8 @@ add_subdirectory("${SYSDIG_DIR}/userspace/libscap" "${PROJECT_BINARY_DIR}/usersp
|
||||
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)
|
||||
set(FALCO_SHARE_DIR ${CMAKE_INSTALL_PREFIX}/share/falco)
|
||||
set(FALCO_BIN_DIR ${CMAKE_INSTALL_PREFIX}/bin)
|
||||
add_subdirectory(scripts)
|
||||
add_subdirectory(userspace/engine)
|
||||
add_subdirectory(userspace/falco)
|
||||
@@ -423,7 +422,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")
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#### Latest release
|
||||
|
||||
**v0.8.0**
|
||||
**v0.7.0**
|
||||
Read the [change log](https://github.com/draios/falco/blob/dev/CHANGELOG.md)
|
||||
|
||||
Dev Branch: [](https://travis-ci.org/draios/falco)<br />
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
/etc/falco/falco.yaml
|
||||
/etc/falco/falco_rules.yaml
|
||||
/etc/falco/falco_rules.local.yaml
|
||||
37
falco.yaml
37
falco.yaml
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -74,9 +74,6 @@
|
||||
- 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)
|
||||
|
||||
@@ -134,13 +131,7 @@
|
||||
# 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]
|
||||
items: [mesos-health-ch, mesos-docker-ex, mesos-agent, mesos-logrotate, mesos-fetcher]
|
||||
|
||||
- list: http_server_binaries
|
||||
items: [nginx, httpd, httpd-foregroun, lighttpd]
|
||||
@@ -163,7 +154,7 @@
|
||||
condition: proc.name in (rpm_binaries)
|
||||
|
||||
- list: deb_binaries
|
||||
items: [dpkg, dpkg-preconfigu, dpkg-reconfigur, apt, apt-get, aptitude,
|
||||
items: [dpkg, dpkg-preconfigu, apt, apt-get, aptitude,
|
||||
frontend, preinst, add-apt-reposit, apt-auto-remova, apt-key,
|
||||
apt-listchanges, unattended-upgr
|
||||
]
|
||||
@@ -171,7 +162,7 @@
|
||||
# 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: [rpm_binaries, deb_binaries, update-alternat]
|
||||
|
||||
- macro: package_mgmt_procs
|
||||
condition: proc.name in (package_mgmt_binaries)
|
||||
@@ -187,14 +178,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, rename_device]
|
||||
|
||||
- list: aide_wrapper_binaries
|
||||
items: [aide.wrapper, update-aide.con]
|
||||
@@ -202,35 +190,17 @@
|
||||
- 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
|
||||
]
|
||||
items: [sendmail, sendmail-msp, postfix, procmail, exim4, pickup, showq]
|
||||
|
||||
- list: make_binaries
|
||||
items: [make, gmake, cmake]
|
||||
@@ -294,14 +264,6 @@
|
||||
- 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]
|
||||
|
||||
# System users that should never log into a system. Consider adding your own
|
||||
# service users (e.g. 'apache' or 'mysqld') here.
|
||||
- macro: system_users
|
||||
@@ -333,8 +295,8 @@
|
||||
|
||||
- macro: parent_python_running_sdchecks
|
||||
condition: >
|
||||
(proc.pname in (python, python2.7) and
|
||||
(proc.pcmdline contains /opt/draios/bin/sdchecks))
|
||||
(proc.name in (python, python2.7) and
|
||||
(proc.cmdline contains /opt/draios/bin/sdchecks))
|
||||
|
||||
- macro: parent_bro_running_python
|
||||
condition: (proc.pname=python and proc.cmdline contains /usr/share/broctl)
|
||||
@@ -344,78 +306,6 @@
|
||||
(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
|
||||
@@ -437,62 +327,25 @@
|
||||
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,
|
||||
dev_creation_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,
|
||||
gen_resolvconf.)
|
||||
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]
|
||||
|
||||
@@ -517,28 +370,12 @@
|
||||
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])
|
||||
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: >
|
||||
@@ -547,17 +384,13 @@
|
||||
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)
|
||||
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])
|
||||
command=%proc.cmdline file=%fd.name)
|
||||
priority: WARNING
|
||||
tags: [filesystem]
|
||||
|
||||
@@ -637,15 +470,13 @@
|
||||
- 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,
|
||||
nginx, monit, supervisord, dragent, aws, initdb, docker-compose,
|
||||
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
|
||||
npm, cloud-init, toybox, ceph
|
||||
]
|
||||
|
||||
- rule: Run shell untrusted
|
||||
@@ -656,31 +487,16 @@
|
||||
and proc.pname exists
|
||||
and not proc.pname in (cron_binaries, shell_binaries, make_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, gitlab_binaries, mesos_slave_binaries, keepalived_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])
|
||||
cmdline=%proc.cmdline pcmdline=%proc.pcmdline)
|
||||
priority: DEBUG
|
||||
tags: [host, shell]
|
||||
|
||||
@@ -692,8 +508,7 @@
|
||||
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
|
||||
@@ -713,20 +528,6 @@
|
||||
- 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
|
||||
@@ -772,90 +573,27 @@
|
||||
'"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"'
|
||||
'"sh -c echo alive "'
|
||||
]
|
||||
|
||||
# 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,
|
||||
and proc.pname exists
|
||||
and not proc.pname in (shell_binaries, make_binaries, docker_binaries, k8s_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)
|
||||
erl_child_setup, ceph, PM2)
|
||||
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
|
||||
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
|
||||
@@ -880,20 +618,7 @@
|
||||
|
||||
- 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)
|
||||
(user.name=www-data and evt.arg.uid=www-data))
|
||||
|
||||
# sshd, mail programs attempt to setuid to root even when running as non-root. Excluding here to avoid meaningless FPs
|
||||
- rule: Non sudo setuid
|
||||
@@ -901,11 +626,10 @@
|
||||
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
|
||||
evt.type=setuid and evt.dir=> and
|
||||
not user.name=root and not somebody_becoming_themself
|
||||
and not proc.name in (userexec_binaries, mail_binaries, docker_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)
|
||||
@@ -917,18 +641,13 @@
|
||||
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
|
||||
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 gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4])
|
||||
(user=%user.name command=%proc.cmdline parent=%proc.pname)
|
||||
priority: NOTICE
|
||||
tags: [host, users]
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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/"
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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)
|
||||
@@ -304,23 +293,18 @@ function load_rules(rules_content, rules_mgr, verbose, all_events, extra, replac
|
||||
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
|
||||
-- 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
|
||||
end
|
||||
else
|
||||
error ("Unknown rule object: "..table.tostring(v))
|
||||
@@ -520,7 +504,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()
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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}"
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user