Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability to read rules files from directories #348

Merged
merged 2 commits into from
Apr 6, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cpack/debian/conffiles
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/etc/falco/falco.yaml
/etc/falco/falco_rules.yaml
/etc/falco/application_rules.yaml
/etc/falco/rules.available/application_rules.yaml
/etc/falco/falco_rules.local.yaml
6 changes: 5 additions & 1 deletion falco.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
# File(s) containing Falco rules, loaded at startup.
# File(s) or Directories containing Falco rules, loaded at startup.
# The name "rules_file" is only for backwards compatibility.
# If the entry is a file, it will be read directly. If the entry is a directory,
# every file in that directory will be read, in alphabetical order.
#
# falco_rules.yaml ships with the falco package and is overridden with
# every new software version. falco_rules.local.yaml is only created
Expand All @@ -10,6 +13,7 @@
rules_file:
- /etc/falco/falco_rules.yaml
- /etc/falco/falco_rules.local.yaml
- /etc/falco/rules.d

# Whether to output events in json or text
json_output: false
Expand Down
4 changes: 3 additions & 1 deletion rules/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ install(FILES falco_rules.local.yaml
RENAME "${FALCO_LOCAL_RULES_DEST_FILENAME}")

install(FILES application_rules.yaml
DESTINATION "${FALCO_ETC_DIR}"
DESTINATION "/etc/falco/rules.available"
RENAME "${FALCO_APP_RULES_DEST_FILENAME}")

install(DIRECTORY DESTINATION "/etc/falco/rules.d")
endif()

10 changes: 10 additions & 0 deletions test/falco_tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,16 @@ trace_files: !mux
- rules/double_rule.yaml
trace_file: trace_files/cat_write.scap

rules_directory:
detect: True
detect_level:
- WARNING
- INFO
- ERROR
rules_file:
- rules/rules_dir
trace_file: trace_files/cat_write.scap

multiple_rules_suppress_info:
detect: True
detect_level:
Expand Down
14 changes: 14 additions & 0 deletions test/rules/rules_dir/000-single_rule.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
- list: cat_binaries
items: [cat]

- list: cat_capable_binaries
items: [cat_binaries]

- macro: is_cat
condition: proc.name in (cat_capable_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
13 changes: 13 additions & 0 deletions test/rules/rules_dir/001-double_rule.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# This ruleset depends on the is_cat macro defined in single_rule.yaml

- rule: exec_from_cat
desc: A process named cat does execve
condition: evt.type=execve and is_cat
output: "An exec was seen (command=%proc.cmdline)"
priority: ERROR

- rule: access_from_cat
desc: A process named cat does an access
condition: evt.type=access and is_cat
output: "An access was seen (command=%proc.cmdline)"
priority: INFO
73 changes: 72 additions & 1 deletion userspace/falco/configuration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ You should have received a copy of the GNU General Public License
along with falco. If not, see <http://www.gnu.org/licenses/>.
*/

#include <algorithm>

#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

#include "configuration.h"
#include "logger.h"

Expand Down Expand Up @@ -62,7 +69,7 @@ void falco_configuration::init(string conf_filename, list<string> &cmdline_optio
struct stat buffer;
if(stat(file.c_str(), &buffer) == 0)
{
m_rules_filenames.push_back(file);
read_rules_file_directory(file, m_rules_filenames);
}
}

Expand Down Expand Up @@ -150,6 +157,70 @@ void falco_configuration::init(string conf_filename, list<string> &cmdline_optio
falco_logger::log_syslog = m_config->get_scalar<bool>("log_syslog", true);
}

void falco_configuration::read_rules_file_directory(const string &path, list<string> &rules_filenames)
{
struct stat st;

int rc = stat(path.c_str(), &st);

if(rc != 0)
{
std::cerr << "Could not get info on rules file " << path << ": " << strerror(errno) << std::endl;
exit(-1);
}

if(st.st_mode & S_IFDIR)
{
// It's a directory. Read the contents, sort
// alphabetically, and add every path to
// rules_filenames
vector<string> dir_filenames;

DIR *dir = opendir(path.c_str());

if(!dir)
{
std::cerr << "Could not get read contents of directory " << path << ": " << strerror(errno) << std::endl;
exit(-1);
}

for (struct dirent *ent = readdir(dir); ent; ent = readdir(dir))
{
string efile = path + "/" + ent->d_name;

rc = stat(efile.c_str(), &st);

if(rc != 0)
{
std::cerr << "Could not get info on rules file " << efile << ": " << strerror(errno) << std::endl;
exit(-1);
}

if(st.st_mode & S_IFREG)
{
dir_filenames.push_back(efile);
}
}

closedir(dir);

std::sort(dir_filenames.begin(),
dir_filenames.end());

for (string &ent : dir_filenames)
{
rules_filenames.push_back(ent);
}
}
else
{
// Assume it's a file and just add to
// rules_filenames. If it can't be opened/etc that
// will be reported later..
rules_filenames.push_back(path);
}
}

static bool split(const string &str, char delim, pair<string,string> &parts)
{
size_t pos;
Expand Down
2 changes: 2 additions & 0 deletions userspace/falco/configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,8 @@ class falco_configuration
void init(std::string conf_filename, std::list<std::string> &cmdline_options);
void init(std::list<std::string> &cmdline_options);

static void read_rules_file_directory(const string &path, list<string> &rules_filenames);

std::list<std::string> m_rules_filenames;
bool m_json_output;
bool m_json_include_output_property;
Expand Down
17 changes: 12 additions & 5 deletions userspace/falco/falco.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,9 @@ static void usage()
" of %%container.info in rule output fields\n"
" See the examples section below for more info.\n"
" -P, --pidfile <pid_file> When run as a daemon, write pid to specified file\n"
" -r <rules_file> Rules file (defaults to value set in configuration file, or /etc/falco_rules.yaml).\n"
" Can be specified multiple times to read from multiple files.\n"
" -r <rules_file> Rules file/directory (defaults to value set in configuration file,\n"
" or /etc/falco_rules.yaml). Can be specified multiple times to read\n"
" from multiple files/directories.\n"
" -s <stats_file> If specified, write statistics related to falco's reading/processing of events\n"
" to this file. (Only useful in live mode).\n"
" -T <tag> Disable any rules with a tag=<tag>. Can be specified multiple times.\n"
Expand Down Expand Up @@ -378,7 +379,7 @@ int falco_init(int argc, char **argv)
}
break;
case 'r':
rules_filenames.push_back(optarg);
falco_configuration::read_rules_file_directory(string(optarg), rules_filenames);
break;
case 's':
stats_filename = optarg;
Expand Down Expand Up @@ -503,13 +504,19 @@ int falco_init(int argc, char **argv)

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");
throw std::invalid_argument("You must specify at least one rules file/directory via -r or a rules_file entry in falco.yaml");
}

falco_logger::log(LOG_DEBUG, "Configured rules filenames:\n");
for (auto filename : config.m_rules_filenames)
{
falco_logger::log(LOG_DEBUG, string(" ") + filename + "\n");
}

for (auto filename : config.m_rules_filenames)
{
falco_logger::log(LOG_INFO, "Loading rules from file " + filename + ":\n");
engine->load_rules_file(filename, verbose, all_events);
falco_logger::log(LOG_INFO, "Parsed rules from file " + filename + "\n");
}

// You can't both disable and enable rules
Expand Down