Add daemonization, fix any bugs found.

Add support for daemonizing via the --daemon flag. If daemonized, the
pid is written to the file provided via the --pidfile flag. When
daemonized, falco immediately returns an error if stderr output or
logging was chosen on the command line.

Clean up handling of outputs to match the expected use case (daemon):

 - syslog output is enabled by default
 - stdout output is disabled by default
 - If not configured at all, both outputs are enabled.

Also fix some bugs I found while running via packages:

 - There were still some references to the old rules filename
   falco_rules.conf.
 - The redhat package mistakenly defined some system directories like
   /etc, /etc/init.d. Add them to the exclusion list (See
   https://cmake.org/Bug/view.php?id=13609 for context).
 - Clean up some of the error messages to be more consistent.

After this I was able to build and install debian and rpm
packages. Starting the falco service ran falco as a daemon with syslog
output.
This commit is contained in:
Mark Stemm 2016-05-06 17:25:54 -07:00
parent cfc89127e7
commit a787dc84d5
7 changed files with 98 additions and 20 deletions

View File

@ -209,6 +209,7 @@ set(CPACK_RPM_PACKAGE_REQUIRES "sysdig")
set(CPACK_RPM_POST_INSTALL_SCRIPT_FILE "${PROJECT_SOURCE_DIR}/scripts/rpm/postinstall") set(CPACK_RPM_POST_INSTALL_SCRIPT_FILE "${PROJECT_SOURCE_DIR}/scripts/rpm/postinstall")
set(CPACK_RPM_PRE_UNINSTALL_SCRIPT_FILE "${PROJECT_SOURCE_DIR}/scripts/rpm/preuninstall") set(CPACK_RPM_PRE_UNINSTALL_SCRIPT_FILE "${PROJECT_SOURCE_DIR}/scripts/rpm/preuninstall")
set(CPACK_RPM_POST_UNINSTALL_SCRIPT_FILE "${PROJECT_SOURCE_DIR}/scripts/rpm/postuninstall") set(CPACK_RPM_POST_UNINSTALL_SCRIPT_FILE "${PROJECT_SOURCE_DIR}/scripts/rpm/postuninstall")
set(CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION /usr/src /usr/share/man /usr/share/man/man8) set(CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION /usr/src /usr/share/man /usr/share/man/man8 /etc /usr /usr/bin /usr/share /etc/rc.d /etc/rc.d/init.d )
set(CPACK_RPM_PACKAGE_RELOCATABLE "OFF")
include(CPack) include(CPack)

View File

@ -1,16 +1,16 @@
rules_file: /etc/falco_rules.conf rules_file: /etc/falco_rules.yaml
json_output: false json_output: false
log_stderr: true log_stderr: true
log_syslog: true log_syslog: true
syslog_output: syslog_output:
enabled: false enabled: true
file_output: file_output:
enabled: true enabled: true
filename: ./events.txt filename: ./events.txt
stdout_output: stdout_output:
enabled: true enabled: false

View File

@ -20,7 +20,7 @@ DESC="Falco"
NAME=falco NAME=falco
DAEMON=/usr/bin/$NAME DAEMON=/usr/bin/$NAME
PIDFILE=/var/run/$NAME.pid PIDFILE=/var/run/$NAME.pid
DAEMON_ARGS="--daemon --falcopid=$PIDFILE" DAEMON_ARGS="--daemon --pidfile=$PIDFILE"
SCRIPTNAME=/etc/init.d/$NAME SCRIPTNAME=/etc/init.d/$NAME
# Exit if the package is not installed # Exit if the package is not installed

View File

@ -34,7 +34,7 @@ start() {
[ -x $exec ] || exit 5 [ -x $exec ] || exit 5
# [ -f $config ] || exit 6 # [ -f $config ] || exit 6
echo -n $"Starting $prog: " echo -n $"Starting $prog: "
daemon $exec --daemon --falcopid=$pidfile daemon $exec --daemon --pidfile=$pidfile
retval=$? retval=$?
echo echo
[ $retval -eq 0 ] && touch $lockfile [ $retval -eq 0 ] && touch $lockfile

View File

@ -19,7 +19,7 @@ void falco_configuration::init(string conf_filename)
string m_config_file = conf_filename; string m_config_file = conf_filename;
m_config = new yaml_configuration(m_config_file); m_config = new yaml_configuration(m_config_file);
m_rules_filename = m_config->get_scalar<string>("rules_file", "/etc/falco_rules.conf"); m_rules_filename = m_config->get_scalar<string>("rules_file", "/etc/falco_rules.yaml");
m_json_output = m_config->get_scalar<bool>("json_output", false); m_json_output = m_config->get_scalar<bool>("json_output", false);
output_config file_output; output_config file_output;

View File

@ -39,11 +39,13 @@ static void usage()
printf( printf(
"Usage: falco [options] rules_filename\n\n" "Usage: falco [options] rules_filename\n\n"
"Options:\n" "Options:\n"
" -h, --help Print this page\n" " -h, --help Print this page\n"
" -c Configuration file (default " FALCO_SOURCE_CONF_FILE ", " FALCO_INSTALL_CONF_FILE ")\n" " -c Configuration file (default " FALCO_SOURCE_CONF_FILE ", " FALCO_INSTALL_CONF_FILE ")\n"
" -o Output type (options are 'stdout', 'syslog', default is 'stdout')\n" " -o Output type (options are 'stdout', 'syslog', default is 'stdout')\n"
" -e <events_file> Read the events from <events_file> (in .scap format) instead of tapping into live.\n" " -d, --daemon Run as a daemon\n"
" -r <rules_file> Rules file (defaults to value set in configuration file, or /etc/falco_rules.conf).\n" " -p, --pidfile <pid_file> When run as a daemon, write pid to specified file\n"
" -e <events_file> Read the events from <events_file> (in .scap format) instead of tapping into live.\n"
" -r <rules_file> Rules file (defaults to value set in configuration file, or /etc/falco_rules.conf).\n"
"\n" "\n"
); );
} }
@ -192,16 +194,20 @@ int falco_init(int argc, char **argv)
sinsp_evt::param_fmt event_buffer_format; sinsp_evt::param_fmt event_buffer_format;
int long_index = 0; int long_index = 0;
string lua_main_filename; string lua_main_filename;
string output_name = "stdout"; string output_name = "syslog";
string scap_filename; string scap_filename;
string conf_filename; string conf_filename;
string rules_filename; string rules_filename;
string lua_dir = FALCO_LUA_DIR; string lua_dir = FALCO_LUA_DIR;
lua_State* ls = NULL; lua_State* ls = NULL;
bool daemon = false;
string pidfilename = "/var/run/falco.pid";
static struct option long_options[] = static struct option long_options[] =
{ {
{"help", no_argument, 0, 'h' }, {"help", no_argument, 0, 'h' },
{"daemon", no_argument, 0, 'd' },
{"pidfile", required_argument, 0, 'd' },
{0, 0, 0, 0} {0, 0, 0, 0}
}; };
@ -214,7 +220,7 @@ int falco_init(int argc, char **argv)
// Parse the args // Parse the args
// //
while((op = getopt_long(argc, argv, while((op = getopt_long(argc, argv,
"c:ho:e:r:", "c:ho:e:r:dp:",
long_options, &long_index)) != -1) long_options, &long_index)) != -1)
{ {
switch(op) switch(op)
@ -239,6 +245,12 @@ int falco_init(int argc, char **argv)
case 'r': case 'r':
rules_filename = optarg; rules_filename = optarg;
break; break;
case 'd':
daemon = true;
break;
case 'p':
pidfilename = optarg;
break;
case '?': case '?':
result = EXIT_FAILURE; result = EXIT_FAILURE;
goto exit; goto exit;
@ -248,6 +260,18 @@ int falco_init(int argc, char **argv)
} }
// Some combinations of arguments are not allowed.
if (daemon && pidfilename == "") {
falco_logger::log(LOG_ERR, "If -d is provided, a pid file must also be provided. Exiting.\n");
result = EXIT_FAILURE;
goto exit;
}
if (daemon && output_name == "stdout") {
falco_logger::log(LOG_ERR, "If -d is provided, can not output to stdout. Exiting.\n");
result = EXIT_FAILURE;
goto exit;
}
ifstream* conf_stream; ifstream* conf_stream;
if (conf_filename.size()) if (conf_filename.size())
@ -255,7 +279,7 @@ int falco_init(int argc, char **argv)
conf_stream = new ifstream(conf_filename); conf_stream = new ifstream(conf_filename);
if (!conf_stream->good()) if (!conf_stream->good())
{ {
falco_logger::log(LOG_ERR, "Could not find configuration file at " + conf_filename + ". Exiting \n"); falco_logger::log(LOG_ERR, "Could not find configuration file at " + conf_filename + ". Exiting.\n");
result = EXIT_FAILURE; result = EXIT_FAILURE;
goto exit; goto exit;
} }
@ -308,7 +332,7 @@ int falco_init(int argc, char **argv)
{ {
falco_logger::log(LOG_ERR, "Could not find Falco Lua libraries (tried " + falco_logger::log(LOG_ERR, "Could not find Falco Lua libraries (tried " +
string(FALCO_LUA_DIR FALCO_LUA_MAIN) + ", " + string(FALCO_LUA_DIR FALCO_LUA_MAIN) + ", " +
lua_main_filename + "). Exiting \n"); lua_main_filename + "). Exiting.\n");
result = EXIT_FAILURE; result = EXIT_FAILURE;
goto exit; goto exit;
} }
@ -365,11 +389,64 @@ int falco_init(int argc, char **argv)
{ {
if(system("modprobe " PROBE_NAME " > /dev/null 2> /dev/null")) if(system("modprobe " PROBE_NAME " > /dev/null 2> /dev/null"))
{ {
falco_logger::log(LOG_ERR, "Unable to load the driver. Exiting\n"); falco_logger::log(LOG_ERR, "Unable to load the driver. Exiting.\n");
} }
inspector->open(); inspector->open();
} }
} }
// If daemonizing, do it here so any init errors will
// be returned in the foreground process.
if (daemon) {
pid_t pid, sid;
pid = fork();
if (pid < 0) {
// error
falco_logger::log(LOG_ERR, "Could not fork. Exiting.\n");
result = EXIT_FAILURE;
goto exit;
} else if (pid > 0) {
// parent. Write child pid to pidfile and exit
std::ofstream pidfile;
pidfile.open(pidfilename);
if (!pidfile.good())
{
falco_logger::log(LOG_ERR, "Could not write pid to pid file " + pidfilename + ". Exiting.\n");
result = EXIT_FAILURE;
goto exit;
}
pidfile << pid;
pidfile.close();
goto exit;
}
// if here, child.
// Become own process group.
sid = setsid();
if (sid < 0) {
falco_logger::log(LOG_ERR, "Could not set session id. Exiting.\n");
result = EXIT_FAILURE;
goto exit;
}
// Set umask so no files are world anything or group writable.
umask(027);
// Change working directory to '/'
if ((chdir("/")) < 0) {
falco_logger::log(LOG_ERR, "Could not change working directory to '/'. Exiting.\n");
result = EXIT_FAILURE;
goto exit;
}
// Close stdin, stdout, stderr.
close(0);
close(1);
close(2);
}
do_inspect(inspector, do_inspect(inspector,
rules, rules,
ls); ls);
@ -378,7 +455,7 @@ int falco_init(int argc, char **argv)
} }
catch(sinsp_exception& e) catch(sinsp_exception& e)
{ {
falco_logger::log(LOG_ERR, "Runtime error: " + string(e.what()) + ". Exiting\n"); falco_logger::log(LOG_ERR, "Runtime error: " + string(e.what()) + ". Exiting.\n");
result = EXIT_FAILURE; result = EXIT_FAILURE;
} }

View File

@ -30,8 +30,8 @@ int falco_logger::syslog(lua_State *ls) {
return 0; return 0;
} }
bool falco_logger::log_stderr; bool falco_logger::log_stderr = true;
bool falco_logger::log_syslog; bool falco_logger::log_syslog = true;
void falco_logger::log(int priority, const string msg) { void falco_logger::log(int priority, const string msg) {
if (falco_logger::log_syslog) { if (falco_logger::log_syslog) {