in auoms.cpp [91:575]
int main(int argc, char**argv) {
std::string config_file = AUOMS_CONF;
bool netlink_only = false;
bool debug_mode = false;
int opt;
while ((opt = getopt(argc, argv, "c:dn")) != -1) {
switch (opt) {
case 'c':
config_file = optarg;
break;
case 'd':
debug_mode = true;
break;
case 'n':
netlink_only = true;
break;
default:
usage();
}
}
if (debug_mode) {
// Enable core dumps
struct rlimit limits;
limits.rlim_cur = RLIM_INFINITY;
limits.rlim_max = RLIM_INFINITY;
setrlimit(RLIMIT_CORE, &limits);
}
Config config;
if (!config_file.empty()) {
try {
config.Load(config_file);
} catch (std::runtime_error& ex) {
Logger::Error("%s", ex.what());
exit(1);
}
}
std::string auditd_path = AUDITD_BIN;
std::string collector_path = AUOMSCOLLECT_EXE;
std::string collector_config_path = "";
std::string outconf_dir = AUOMS_OUTCONF_DIR;
std::string rules_dir = AUOMS_RULES_DIR;
std::string redact_dir = AUOMS_REDACT_DIR;
std::string data_dir = AUOMS_DATA_DIR;
std::string run_dir = AUOMS_RUN_DIR;
uint32_t backlog_limit = 10240;
uint32_t backlog_wait_time = 1;
if (config.HasKey("outconf_dir")) {
outconf_dir = config.GetString("outconf_dir");
}
if (config.HasKey("rules_dir")) {
rules_dir = config.GetString("rules_dir");
}
if (config.HasKey("redact_dir")) {
redact_dir = config.GetString("redact_dir");
}
if (config.HasKey("data_dir")) {
data_dir = config.GetString("data_dir");
}
if (config.HasKey("run_dir")) {
run_dir = config.GetString("run_dir");
}
if (config.HasKey("auditd_path")) {
auditd_path = config.GetString("auditd_path");
}
if (netlink_only) {
auditd_path = "/does/not/exist";
}
if (config.HasKey("collector_path")) {
collector_path = config.GetString("collector_path");
}
if (config.HasKey("collector_config_path")) {
collector_config_path = config.GetString("collector_config_path");
}
if (config.HasKey("backlog_limit")) {
backlog_limit = static_cast<uint32_t>(config.GetUint64("backlog_limit"));
}
if (config.HasKey("backlog_wait_time")) {
backlog_wait_time = static_cast<uint32_t>(config.GetUint64("backlog_wait_time"));
}
std::string input_socket_path = run_dir + "/input.socket";
std::string status_socket_path = run_dir + "/status.socket";
if (config.HasKey("input_socket_path")) {
input_socket_path = config.GetString("input_socket_path");
}
if (config.HasKey("status_socket_path")) {
status_socket_path = config.GetString("status_socket_path");
}
int num_priorities = 8;
size_t max_file_data_size = 1024*1024;
size_t max_unsaved_files = 128;
size_t max_fs_bytes = 1024*1024*1024;
double max_fs_pct = 10;
double min_fs_free_pct = 5;
long save_delay = 250;
std::string queue_dir = data_dir + "/queue";
if (config.HasKey("queue_dir")) {
queue_dir = config.GetString("queue_dir");
}
if (queue_dir.empty()) {
Logger::Error("Invalid 'queue_file' value");
exit(1);
}
if (config.HasKey("queue_num_priorities")) {
num_priorities = config.GetUint64("queue_num_priorities");
}
if (config.HasKey("queue_max_file_data_size")) {
max_file_data_size = config.GetUint64("queue_max_file_data_size");
}
if (config.HasKey("queue_max_unsaved_files")) {
max_unsaved_files = config.GetUint64("queue_max_unsaved_files");
}
if (config.HasKey("queue_max_fs_bytes")) {
max_fs_bytes = config.GetUint64("queue_max_fs_bytes");
}
if (config.HasKey("queue_max_fs_pct")) {
max_fs_pct = config.GetDouble("queue_max_fs_pct");
}
if (config.HasKey("queue_min_fs_free_pct")) {
min_fs_free_pct = config.GetDouble("queue_min_fs_free_pct");
}
if (config.HasKey("queue_save_delay")) {
save_delay = config.GetUint64("queue_save_delay");
}
std::string lock_file = data_dir + "/auoms.lock";
if (config.HasKey("lock_file")) {
lock_file = config.GetString("lock_file");
}
uint64_t rss_limit = 1024L*1024L*1024L;
uint64_t virt_limit = 4096L*1024L*1024L;
double rss_pct_limit = 5;
if (config.HasKey("rss_limit")) {
rss_limit = config.GetUint64("rss_limit");
}
if (config.HasKey("rss_pct_limit")) {
rss_pct_limit = config.GetDouble("rss_pct_limit");
}
if (config.HasKey("virt_limit")) {
virt_limit = config.GetUint64("virt_limit");
}
bool use_syslog = true;
if (config.HasKey("use_syslog")) {
use_syslog = config.GetBool("use_syslog");
}
if (use_syslog) {
Logger::OpenSyslog("auoms", LOG_DAEMON);
}
bool disable_cgroups = false;
if (config.HasKey("disable_cgroups")) {
disable_cgroups = config.GetBool("disable_cgroups");
}
// Set cgroup defaults
if (!config.HasKey(CPU_SOFT_LIMIT_NAME)) {
config.SetString(CPU_SOFT_LIMIT_NAME, "5");
}
if (!config.HasKey(CPU_HARD_LIMIT_NAME)) {
config.SetString(CPU_HARD_LIMIT_NAME, "25");
}
bool disable_event_filtering = false;
if (config.HasKey("disable_event_filtering")) {
disable_event_filtering = config.GetBool("disable_event_filtering");
}
// Set EventPrioritizer defaults
if (!config.HasKey("event_priority_by_syscall")) {
config.SetString("event_priority_by_syscall", R"json({"execve":2,"execveat":2,"*":3})json");
}
if (!config.HasKey("event_priority_by_record_type")) {
config.SetString("event_priority_by_record_type", R"json({"AUOMS_EXECVE":2,"AUOMS_SYSCALL":3,"AUOMS_PROCESS_INVENTORY":1})json");
}
if (!config.HasKey("event_priority_by_record_type_category")) {
config.SetString("event_priority_by_record_type_category", R"json({"AUOMS_MSG":0, "USER_MSG":1,"SELINUX":1,"APPARMOR":1})json");
}
int default_priority = 4;
if (config.HasKey("default_event_priority")) {
default_priority = static_cast<uint16_t>(config.GetUint64("default_event_priority"));
}
if (default_priority > num_priorities-1) {
default_priority = num_priorities-1;
}
auto event_prioritizer = std::make_shared<EventPrioritizer>(default_priority);
if (!event_prioritizer->LoadFromConfig(config)) {
Logger::Error("Failed to load EventPrioritizer config, exiting");
exit(1);
}
Logger::Info("Trying to acquire singleton lock");
LockFile singleton_lock(lock_file);
switch(singleton_lock.Lock()) {
case LockFile::FAILED:
Logger::Error("Failed to acquire singleton lock (%s): %s", lock_file.c_str(), std::strerror(errno));
exit(1);
break;
case LockFile::PREVIOUSLY_ABANDONED:
Logger::Warn("Previous instance did not exit cleanly");
break;
case LockFile::INTERRUPTED:
Logger::Error("Failed to acquire singleton lock (%s): Interrupted", lock_file.c_str());
exit(1);
break;
}
Logger::Info("Acquire singleton lock");
std::shared_ptr<CGroupCPU> cgcpu;
if (!disable_cgroups) {
try {
cgcpu = CPULimits::CGFromConfig(config, "auoms");
// systemd may not have put auoms into the default cgroup at this point
// Wait a few seconds before moving into the right cgroup so we avoid getting moved back out by systemd
std::thread cg_thread([&cgcpu]() {
Signals::InitThread();
int sleep_time = 5;
// Loop forever to make sure we stay in our cgroup
while (!Signals::IsExit()) {
sleep(sleep_time);
sleep_time = 60;
try {
cgcpu->AddSelf();
} catch (const std::exception &ex) {
Logger::Error("Failed to configure cpu cgroup: %s", ex.what());
Logger::Warn("CPU Limits cannot be enforced");
return;
}
}
});
cg_thread.detach();
} catch (std::runtime_error &ex) {
Logger::Error("Failed to configure cpu cgroup: %s", ex.what());
Logger::Warn("CPU Limits cannot be enforced");
}
}
// This will block signals like SIGINT and SIGTERM
// They will be handled once Signals::Start() is called.
Signals::Init();
Logger::Info("Opening queue: %s", queue_dir.c_str());
auto queue = PriorityQueue::Open(queue_dir, num_priorities, max_file_data_size, max_unsaved_files, max_fs_bytes, max_fs_pct, min_fs_free_pct);
if (!queue) {
Logger::Error("Failed to open queue '%s'", queue_dir.c_str());
exit(1);
}
auto operational_status = std::make_shared<OperationalStatus>(status_socket_path, queue);
if (!operational_status->Initialize()) {
Logger::Error("Failed to initialize OperationalStatus");
exit(1);
}
operational_status->Start();
auto cmdline_redactor = std::make_shared<CmdlineRedactor>();
cmdline_redactor->LoadFromDir(redact_dir, true);
std::thread rule_thread([&redact_dir, &cmdline_redactor, &operational_status]() {
Signals::InitThread();
int sleep_time = 1;
// Loop forever until required rules are successfully loaded.
while (!Signals::IsExit()) {
if (cmdline_redactor->LoadFromDir(redact_dir, true)) {
operational_status->ClearErrorCondition(ErrorCategory::MISSING_REDACTION_RULES);
operational_status->SetRedactionRules(cmdline_redactor->GetRules());
return;
}
auto missing_rules = join(cmdline_redactor->GetMissingRules(), ", ");
operational_status->SetErrorCondition(ErrorCategory::MISSING_REDACTION_RULES, "Missing redaction rules: " + missing_rules);
operational_status->SetRedactionRules(cmdline_redactor->GetRules());
sleep(sleep_time);
sleep_time *= 2;
if (sleep_time > 60) {
sleep_time = 60;
}
}
});
rule_thread.detach();
auto metrics = std::make_shared<Metrics>("auoms", queue);
metrics->Start();
auto syscall_metrics = std::make_shared<SyscallMetrics>(metrics);
syscall_metrics->Start();
auto system_metrics = std::make_shared<SystemMetrics>(metrics);
system_metrics->Start();
auto proc_metrics = std::make_shared<ProcMetrics>("auoms", queue, metrics, rss_limit, virt_limit, rss_pct_limit, []() {
Logger::Error("A memory limit was exceeded, exiting immediately");
exit(1);
});
proc_metrics->Start();
Inputs inputs(input_socket_path, operational_status);
if (!inputs.Initialize()) {
Logger::Error("Failed to initialize inputs");
exit(1);
}
CollectionMonitor collection_monitor(queue, auditd_path, collector_path, collector_config_path);
collection_monitor.Start();
AuditRulesMonitor rules_monitor(rules_dir, backlog_limit, backlog_wait_time, operational_status);
rules_monitor.Start();
auto user_db = std::make_shared<UserDB>();
try {
user_db->Start();
} catch (const std::exception& ex) {
Logger::Error("Unexpected exception during user_db startup: %s", ex.what());
exit(1);
} catch (...) {
Logger::Error("Unexpected exception during user_db startup");
exit(1);
}
std::shared_ptr<FiltersEngine> filtersEngine;
std::shared_ptr<ProcessTree> processTree;
std::shared_ptr<IEventFilterFactory> outputsFilterFactory;
if (!disable_event_filtering) {
filtersEngine = std::make_shared<FiltersEngine>();
processTree = std::make_shared<ProcessTree>(user_db, filtersEngine);
processTree->PopulateTree(); // Pre-populate tree
outputsFilterFactory = std::shared_ptr<IEventFilterFactory>(static_cast<IEventFilterFactory*>(new OutputsEventFilterFactory(user_db, filtersEngine, processTree)));
}
Outputs outputs(queue, outconf_dir, outputsFilterFactory);
std::thread autosave_thread([&]() {
Signals::InitThread();
try {
queue->Saver(save_delay);
} catch (const std::exception& ex) {
Logger::Error("Unexpected exception in autosave thread: %s", ex.what());
exit(1);
}
});
try {
outputs.Start();
} catch (const std::exception& ex) {
Logger::Error("Unexpected exception during outputs startup: %s", ex.what());
exit(1);
} catch (...) {
Logger::Error("Unexpected exception during outputs startup");
exit(1);
}
Signals::SetHupHandler([&outputs,&config_file](){
Config config;
if (config_file.size() > 0) {
try {
config.Load(config_file);
} catch (std::runtime_error& ex) {
Logger::Error("Config error during reload: %s", ex.what());
return;
}
}
outputs.Reload();
});
// Start signal handling thread
Signals::Start();
std::shared_ptr<ProcessNotify> processNotify;
if (!disable_event_filtering) {
processTree->Start();
processNotify = std::make_shared<ProcessNotify>(processTree);
processNotify->Start();
}
auto event_queue = std::make_shared<EventQueue>(queue);
auto builder = std::make_shared<EventBuilder>(event_queue, event_prioritizer);
RawEventProcessor rep(builder, user_db, cmdline_redactor, processTree, filtersEngine, metrics);
inputs.Start();
Signals::SetExitHandler([&inputs]() {
Logger::Info("Stopping inputs");
inputs.Stop();
});
bool remove_lock = true;
try {
Logger::Info("Starting input loop");
while (!Signals::IsExit()) {
if (!inputs.HandleData([&rep](void* ptr, size_t size) {
rep.ProcessData(reinterpret_cast<char*>(ptr), size);
rep.DoProcessInventory();
})) {
break;
};
}
Logger::Info("Input loop stopped");
} catch (const std::exception& ex) {
Logger::Error("Unexpected exception in input loop: %s", ex.what());
remove_lock = false;
} catch (...) {
Logger::Error("Unexpected exception in input loop");
remove_lock = false;
}
Logger::Info("Exiting");
try {
collection_monitor.Stop();
processNotify->Stop();
processTree->Stop();
proc_metrics->Stop();
system_metrics->Stop();
syscall_metrics->Stop();
metrics->Stop();
rules_monitor.Stop();
inputs.Stop();
outputs.Stop(false); // Trigger outputs shutdown but don't block
user_db->Stop(); // Stop user db monitoring
metrics->FlushLogMetrics();
queue->Close(); // Close queue, this will trigger exit of autosave thread
outputs.Wait(); // Wait for outputs to finish shutdown
autosave_thread.join(); // Wait for autosave thread to exit
operational_status->Stop();
} catch (const std::exception& ex) {
Logger::Error("Unexpected exception during exit: %s", ex.what());
exit(1);
} catch (...) {
Logger::Error("Unexpected exception during exit");
exit(1);
}
if (remove_lock) {
singleton_lock.Unlock();
}
exit(0);
}