mirror of
https://github.com/openappsec/openappsec.git
synced 2025-09-29 19:24:26 +03:00
First release of open-appsec source code
This commit is contained in:
4
core/config/CMakeLists.txt
Normal file
4
core/config/CMakeLists.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
add_library(config config.cc config_specific.cc config_globals.cc)
|
||||
target_link_libraries(config agent_core_utilities)
|
||||
|
||||
link_directories(${BOOST_ROOT}/lib)
|
850
core/config/config.cc
Normal file
850
core/config/config.cc
Normal file
@@ -0,0 +1,850 @@
|
||||
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "config.h"
|
||||
#include "config_component.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <boost/regex.hpp>
|
||||
#include "agent_core_utilities.h"
|
||||
|
||||
#include "cereal/archives/json.hpp"
|
||||
|
||||
#include "debug.h"
|
||||
#include "cereal/external/rapidjson/error/en.h"
|
||||
#include "include/profile_settings.h"
|
||||
#include "enum_range.h"
|
||||
#include "rest.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace cereal;
|
||||
using namespace Config;
|
||||
|
||||
USE_DEBUG_FLAG(D_CONFIG);
|
||||
|
||||
AgentProfileSettings AgentProfileSettings::default_profile_settings = AgentProfileSettings();
|
||||
|
||||
class registerExpectedConfigUpdates : public ClientRest
|
||||
{
|
||||
public:
|
||||
C2S_PARAM(string, service_name);
|
||||
C2S_OPTIONAL_PARAM(string, service_id);
|
||||
C2S_PARAM(int, service_listening_port);
|
||||
C2S_PARAM(vector<string>, expected_configurations);
|
||||
S2C_PARAM(bool, status);
|
||||
};
|
||||
|
||||
class LoadNewConfigurationStatus : public ClientRest
|
||||
{
|
||||
public:
|
||||
LoadNewConfigurationStatus(uint _id, bool _error, bool end) : id(_id), error(_error), finished(end) {}
|
||||
|
||||
void setError(const string &error) { error_message = error; }
|
||||
|
||||
private:
|
||||
C2S_PARAM(int, id);
|
||||
C2S_PARAM(bool, error);
|
||||
C2S_PARAM(bool, finished);
|
||||
C2S_OPTIONAL_PARAM(string, error_message);
|
||||
};
|
||||
|
||||
class LoadNewConfiguration : public ServerRest
|
||||
{
|
||||
public:
|
||||
void
|
||||
doCall() override
|
||||
{
|
||||
auto i_config = Singleton::Consume<I_Config>::from<ConfigComponent>();
|
||||
|
||||
I_Config::AsyncLoadConfigStatus load_config_staus = i_config->reloadConfiguration(policy_version, true, id);
|
||||
finished = load_config_staus == I_Config::AsyncLoadConfigStatus::InProgress;
|
||||
error = load_config_staus == I_Config::AsyncLoadConfigStatus::Error;
|
||||
if (!finished) {
|
||||
error_message = "Reload already in progress - can't start another one";
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
BOTH_PARAM(int, id);
|
||||
S2C_PARAM(bool, error);
|
||||
S2C_PARAM(bool, finished);
|
||||
S2C_OPTIONAL_PARAM(string, error_message);
|
||||
C2S_PARAM(string, policy_version);
|
||||
};
|
||||
|
||||
class ConfigComponent::Impl : public Singleton::Provide<I_Config>::From<ConfigComponent>
|
||||
{
|
||||
using PerContextValue = vector<pair<shared_ptr<EnvironmentEvaluator<bool>>, TypeWrapper>>;
|
||||
|
||||
public:
|
||||
void preload();
|
||||
void init();
|
||||
|
||||
const TypeWrapper & getConfiguration(const vector<string> &paths) const override;
|
||||
PerContextValue getAllConfiguration(const std::vector<std::string> &paths) const;
|
||||
const TypeWrapper & getResource(const vector<string> &paths) const override;
|
||||
const TypeWrapper & getSetting(const vector<string> &paths) const override;
|
||||
const string & getProfileAgentSetting(const string &setting_name) const override;
|
||||
vector<string> getProfileAgentSettings(const string ®ex) const override;
|
||||
|
||||
const string & getConfigurationFlag(const string &flag_name) const override;
|
||||
const string & getFilesystemPathConfig() const override;
|
||||
const string & getLogFilesPathConfig() const override;
|
||||
|
||||
string getPolicyConfigPath(const string &name, ConfigFileType type, const string &tenant = "") const override;
|
||||
|
||||
bool setConfiguration(TypeWrapper &&value, const std::vector<std::string> &paths) override;
|
||||
bool setResource(TypeWrapper &&value, const std::vector<std::string> &paths) override;
|
||||
bool setSetting(TypeWrapper &&value, const std::vector<std::string> &paths) override;
|
||||
|
||||
void registerExpectedConfigFile(const string &file_name, ConfigFileType type) override;
|
||||
void registerExpectedConfiguration(unique_ptr<GenericConfig<true>> &&config) override;
|
||||
void registerExpectedResource(unique_ptr<GenericConfig<false>> &&config) override;
|
||||
void registerExpectedSetting(unique_ptr<GenericConfig<false>> &&config) override;
|
||||
|
||||
|
||||
bool loadConfiguration(istream &json_contents) override;
|
||||
bool loadConfiguration(const vector<string> &configuration_flags) override;
|
||||
AsyncLoadConfigStatus reloadConfiguration(const string &version, bool is_async, uint id) override;
|
||||
bool saveConfiguration(ostream &) const override { return false; }
|
||||
|
||||
void registerConfigPrepareCb(ConfigCb) override;
|
||||
void registerConfigLoadCb(ConfigCb) override;
|
||||
void registerConfigAbortCb(ConfigCb) override;
|
||||
|
||||
private:
|
||||
void clearOldTenants();
|
||||
bool isTenantActive(const string &name) const;
|
||||
void periodicRegistrationRefresh();
|
||||
|
||||
bool loadConfiguration(vector<shared_ptr<JSONInputArchive>> &file_archives, bool is_async);
|
||||
bool commitSuccess();
|
||||
bool commitFailure(const string &error);
|
||||
bool reloadConfigurationImpl(const string &version, bool is_async);
|
||||
void reloadConfigurationContinuesWrapper(const string &version, uint id);
|
||||
|
||||
string
|
||||
getActiveTenant() const
|
||||
{
|
||||
auto active_id = Singleton::Consume<I_Environment>::by<ConfigComponent>()->get<string>("ActiveTenantId");
|
||||
|
||||
return active_id.ok() ? *active_id : default_tenant_id;
|
||||
}
|
||||
|
||||
bool
|
||||
sendOrchestatorConfMsg(int env_listening_port)
|
||||
{
|
||||
registerExpectedConfigUpdates config_updates;
|
||||
|
||||
config_updates.service_name = executable_name;
|
||||
config_updates.service_listening_port = env_listening_port;
|
||||
|
||||
|
||||
if (Singleton::exists<I_InstanceAwareness>()) {
|
||||
auto instance_awareness = Singleton::Consume<I_InstanceAwareness>::by<ConfigComponent>();
|
||||
if (instance_awareness->getUniqueID().ok()) {
|
||||
config_updates.service_id = instance_awareness->getUniqueID().unpack();
|
||||
}
|
||||
}
|
||||
|
||||
vector<string> files;
|
||||
files.reserve(expected_configuration_files.size());
|
||||
for (const auto &conf : expected_configuration_files) {
|
||||
files.push_back(conf.first);
|
||||
}
|
||||
config_updates.expected_configurations = move(files);
|
||||
|
||||
I_Messaging *messaging = Singleton::Consume<I_Messaging>::by<ConfigComponent>();
|
||||
::Flags<MessageConnConfig> conn_flags;
|
||||
conn_flags.setFlag(MessageConnConfig::ONE_TIME_CONN);
|
||||
bool is_success = messaging->sendObject(
|
||||
config_updates,
|
||||
I_Messaging::Method::POST,
|
||||
"127.0.0.1",
|
||||
7777, // primary Orchestrator's port
|
||||
conn_flags,
|
||||
"/set-nano-service-config"
|
||||
);
|
||||
if (!is_success) {
|
||||
is_success = messaging->sendObject(
|
||||
config_updates,
|
||||
I_Messaging::Method::POST,
|
||||
"127.0.0.1",
|
||||
7778, // secondary Orchestrator's port
|
||||
conn_flags,
|
||||
"/set-nano-service-config"
|
||||
);
|
||||
}
|
||||
return is_success && config_updates.status.get();
|
||||
}
|
||||
|
||||
void
|
||||
reloadFileSystemPaths()
|
||||
{
|
||||
auto &alternative_conf_path = getConfigurationFlag("configDirectoryPath");
|
||||
if (alternative_conf_path != "") {
|
||||
config_directory_path = alternative_conf_path;
|
||||
} else {
|
||||
filesystem_prefix = getConfigurationFlag("filesystem_path") != "" ?
|
||||
getConfigurationFlag("filesystem_path") :
|
||||
"/etc/cp";
|
||||
log_files_prefix = getConfigurationFlag("log_files_path") != "" ?
|
||||
getConfigurationFlag("log_files_path") :
|
||||
"/var/log";
|
||||
config_directory_path = filesystem_prefix + default_config_directory_path;
|
||||
}
|
||||
dbgTrace(D_CONFIG) << "File system path reloaded: " << config_directory_path;
|
||||
}
|
||||
|
||||
void
|
||||
sendOrchestatorReloadStatusMsg(const LoadNewConfigurationStatus &status)
|
||||
{
|
||||
I_Messaging *messaging = Singleton::Consume<I_Messaging>::by<ConfigComponent>();
|
||||
::Flags<MessageConnConfig> conn_flags;
|
||||
conn_flags.setFlag(MessageConnConfig::ONE_TIME_CONN);
|
||||
bool is_success = messaging->sendNoReplyObject(
|
||||
status,
|
||||
I_Messaging::Method::POST,
|
||||
"127.0.0.1",
|
||||
7777, // primary Orchestrator's port
|
||||
conn_flags,
|
||||
"/set-reconf-status"
|
||||
);
|
||||
if (!is_success) {
|
||||
messaging->sendNoReplyObject(
|
||||
status,
|
||||
I_Messaging::Method::POST,
|
||||
"127.0.0.1",
|
||||
7778, // secondary Orchestrator's port
|
||||
conn_flags,
|
||||
"/set-reconf-status"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
unordered_map<string, map<vector<string>, PerContextValue>> configuration_nodes;
|
||||
unordered_map<string, map<vector<string>, TypeWrapper>> settings_nodes;
|
||||
map<string, string> profile_settings;
|
||||
unordered_map<string, string> config_flags;
|
||||
|
||||
map<vector<string>, TypeWrapper> new_resource_nodes;
|
||||
unordered_map<string, map<vector<string>, PerContextValue>> new_configuration_nodes;
|
||||
unordered_map<string, map<vector<string>, TypeWrapper>> new_settings_nodes;
|
||||
unordered_map<string, string> new_config_flags;
|
||||
|
||||
set<unique_ptr<GenericConfig<true>>> expected_configs;
|
||||
set<unique_ptr<GenericConfig<false>>> expected_resources;
|
||||
set<unique_ptr<GenericConfig<false>>> expected_settings;
|
||||
map<string, set<ConfigFileType>> expected_configuration_files;
|
||||
set<string> config_file_paths;
|
||||
|
||||
I_TenantManager *tenant_mananger = nullptr;
|
||||
|
||||
vector<ConfigCb> configuration_prepare_cbs;
|
||||
vector<ConfigCb> configuration_commit_cbs;
|
||||
vector<ConfigCb> configuration_abort_cbs;
|
||||
|
||||
bool is_continuous_report = false;
|
||||
const string default_tenant_id = "default_tenant_id_value";
|
||||
string executable_name = "";
|
||||
string filesystem_prefix = "/etc/cp";
|
||||
string log_files_prefix = "/var/log";
|
||||
string default_config_directory_path = "/conf/";
|
||||
string config_directory_path = "";
|
||||
|
||||
TypeWrapper empty;
|
||||
};
|
||||
|
||||
void
|
||||
ConfigComponent::Impl::preload()
|
||||
{
|
||||
I_Environment *environment = Singleton::Consume<I_Environment>::by<ConfigComponent>();
|
||||
auto executable = environment->get<string>("Executable Name");
|
||||
if (!executable.ok() || *executable == "") {
|
||||
dbgWarning(D_CONFIG)
|
||||
<< "Could not load nano service's settings since \"Executable Name\" in not found in the environment";
|
||||
return;
|
||||
}
|
||||
|
||||
executable_name = *executable;
|
||||
auto file_path_end = executable_name.find_last_of("/");
|
||||
if (file_path_end != string::npos) {
|
||||
executable_name = executable_name.substr(file_path_end + 1);
|
||||
}
|
||||
auto file_sufix_start = executable_name.find_first_of(".");
|
||||
if (file_sufix_start != string::npos) {
|
||||
executable_name = executable_name.substr(0, file_sufix_start);
|
||||
}
|
||||
|
||||
config_file_paths.insert(executable_name + "-conf.json");
|
||||
config_file_paths.insert(executable_name + "-debug-conf.json");
|
||||
config_file_paths.insert("settings.json");
|
||||
}
|
||||
|
||||
void
|
||||
ConfigComponent::Impl::init()
|
||||
{
|
||||
reloadFileSystemPaths();
|
||||
tenant_mananger = Singleton::Consume<I_TenantManager>::by<ConfigComponent>();
|
||||
|
||||
if (!Singleton::exists<I_MainLoop>()) return;
|
||||
auto mainloop = Singleton::Consume<I_MainLoop>::by<ConfigComponent>();
|
||||
|
||||
if (executable_name != "cp-nano-orchestration") {
|
||||
mainloop->addOneTimeRoutine(
|
||||
I_MainLoop::RoutineType::System,
|
||||
[this] () { periodicRegistrationRefresh(); },
|
||||
"Configuration update registration",
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
mainloop->addRecurringRoutine(
|
||||
I_MainLoop::RoutineType::System,
|
||||
tenant_mananger->getTimeoutVal(),
|
||||
[this] () { clearOldTenants(); },
|
||||
"Config comp old tenant cleanup"
|
||||
);
|
||||
}
|
||||
|
||||
static
|
||||
bool
|
||||
checkContext(const shared_ptr<EnvironmentEvaluator<bool>> &ctx)
|
||||
{
|
||||
if (ctx == nullptr) return true;
|
||||
auto res = ctx->evalVariable();
|
||||
return res.ok() && *res;
|
||||
}
|
||||
|
||||
const TypeWrapper &
|
||||
ConfigComponent::Impl::getConfiguration(const vector<string> &paths) const
|
||||
{
|
||||
auto curr_configs = configuration_nodes.find(getActiveTenant());
|
||||
|
||||
if (curr_configs != configuration_nodes.end()) {
|
||||
auto requested_config = curr_configs->second.find(paths);
|
||||
if (requested_config != curr_configs->second.end()) {
|
||||
for (auto &value : requested_config->second) {
|
||||
if (checkContext(value.first)) return value.second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto global_config = configuration_nodes.find(default_tenant_id);
|
||||
if (global_config != configuration_nodes.end()) {
|
||||
auto requested_config = global_config->second.find(paths);
|
||||
if (requested_config != global_config->second.end()) {
|
||||
for (auto &value : requested_config->second) {
|
||||
if (checkContext(value.first)) return value.second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return empty;
|
||||
}
|
||||
|
||||
vector<pair<shared_ptr<EnvironmentEvaluator<bool>>, TypeWrapper>>
|
||||
ConfigComponent::Impl::getAllConfiguration(const vector<string> &paths) const
|
||||
{
|
||||
auto curr_configs = configuration_nodes.find(getActiveTenant());
|
||||
|
||||
if (curr_configs != configuration_nodes.end()) {
|
||||
auto requested_config = curr_configs->second.find(paths);
|
||||
if (requested_config != curr_configs->second.end()) return requested_config->second;
|
||||
}
|
||||
|
||||
auto global_config = configuration_nodes.find(default_tenant_id);
|
||||
if (global_config != configuration_nodes.end()) {
|
||||
auto requested_config = global_config->second.find(paths);
|
||||
if (requested_config != global_config->second.end()) return requested_config->second;
|
||||
}
|
||||
|
||||
return vector<pair<shared_ptr<EnvironmentEvaluator<bool>>, TypeWrapper>>();
|
||||
}
|
||||
|
||||
const TypeWrapper &
|
||||
ConfigComponent::Impl::getResource(const vector<string> &paths) const
|
||||
{
|
||||
auto requested_resource = new_resource_nodes.find(paths);
|
||||
if (requested_resource != new_resource_nodes.end()) return requested_resource->second;
|
||||
|
||||
return empty;
|
||||
}
|
||||
|
||||
const TypeWrapper &
|
||||
ConfigComponent::Impl::getSetting(const vector<string> &paths) const
|
||||
{
|
||||
auto curr_configs = settings_nodes.find(getActiveTenant());
|
||||
if (curr_configs != settings_nodes.end()) {
|
||||
auto requested_config = curr_configs->second.find(paths);
|
||||
if (requested_config != curr_configs->second.end()) return requested_config->second;
|
||||
}
|
||||
|
||||
auto global_config = settings_nodes.find(default_tenant_id);
|
||||
if (global_config != settings_nodes.end()) {
|
||||
auto requested_config = global_config->second.find(paths);
|
||||
if (requested_config != global_config->second.end()) return requested_config->second;
|
||||
}
|
||||
|
||||
return empty;
|
||||
}
|
||||
|
||||
const string &
|
||||
ConfigComponent::Impl::getProfileAgentSetting(const string &setting_name) const
|
||||
{
|
||||
auto setting_raw_val = profile_settings.find(setting_name);
|
||||
if (setting_raw_val != profile_settings.end()) return setting_raw_val->second;
|
||||
|
||||
const static string not_found = "";
|
||||
return not_found;
|
||||
}
|
||||
|
||||
const string &
|
||||
ConfigComponent::Impl::getConfigurationFlag(const string &flag_name) const
|
||||
{
|
||||
auto flag = new_config_flags.find(flag_name);
|
||||
if (flag != new_config_flags.end()) return flag->second;
|
||||
|
||||
flag = config_flags.find(flag_name);
|
||||
if (flag != config_flags.end()) return flag->second;
|
||||
|
||||
const static string not_found = "";
|
||||
return not_found;
|
||||
}
|
||||
|
||||
const string &
|
||||
ConfigComponent::Impl::getFilesystemPathConfig() const
|
||||
{
|
||||
dbgTrace(D_CONFIG) << "config get filesystem: " << filesystem_prefix;
|
||||
return filesystem_prefix;
|
||||
}
|
||||
|
||||
const string &
|
||||
ConfigComponent::Impl::getLogFilesPathConfig() const
|
||||
{
|
||||
dbgTrace(D_CONFIG) << "config get log_files_prefix: " << log_files_prefix;
|
||||
return log_files_prefix;
|
||||
}
|
||||
|
||||
string
|
||||
ConfigComponent::Impl::getPolicyConfigPath(const string &config_name, ConfigFileType type, const string &tenant) const
|
||||
{
|
||||
static const string policy_suffix = ".policy";
|
||||
static const string data_suffix = ".data";
|
||||
static const string tenant_prefix = "tenant_";
|
||||
|
||||
string base_path =
|
||||
getConfigurationWithDefault(config_directory_path, "Config Component", "configuration path") +
|
||||
(tenant.empty() ? "" : tenant_prefix + tenant + "/");
|
||||
|
||||
switch (type) {
|
||||
case ConfigFileType::Data: return base_path + "data/" + config_name + data_suffix;
|
||||
case ConfigFileType::RawData: return base_path + "data/" + config_name + data_suffix;
|
||||
case ConfigFileType::Policy: return base_path + config_name + "/" + config_name + policy_suffix;
|
||||
case ConfigFileType::COUNT: break;
|
||||
}
|
||||
|
||||
dbgError(D_CONFIG) << "Received illegal configuration file type " << static_cast<uint>(type);
|
||||
return "";
|
||||
}
|
||||
|
||||
bool
|
||||
ConfigComponent::Impl::setConfiguration(TypeWrapper &&value, const vector<string> &paths)
|
||||
{
|
||||
for (auto &tennant : configuration_nodes) {
|
||||
tennant.second.erase(paths);
|
||||
}
|
||||
|
||||
PerContextValue value_vec;
|
||||
value_vec.emplace_back(nullptr, move(value));
|
||||
configuration_nodes[default_tenant_id][paths] = move(value_vec);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ConfigComponent::Impl::setResource(TypeWrapper &&value, const vector<string> &paths)
|
||||
{
|
||||
new_resource_nodes[paths] = move(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ConfigComponent::Impl::setSetting(TypeWrapper &&value, const vector<string> &paths)
|
||||
{
|
||||
settings_nodes[default_tenant_id][paths] = move(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
vector<string>
|
||||
ConfigComponent::Impl::getProfileAgentSettings(const string ®ex) const
|
||||
{
|
||||
vector<string> setting_raw_values;
|
||||
boost::regex reg(regex);
|
||||
for (auto &setting : profile_settings) {
|
||||
if (NGEN::Regex::regexMatch(__FILE__, __LINE__, setting.first, reg)) {
|
||||
setting_raw_values.push_back(setting.second);
|
||||
}
|
||||
}
|
||||
|
||||
return setting_raw_values;
|
||||
}
|
||||
|
||||
void
|
||||
ConfigComponent::Impl::registerExpectedConfigFile(const string &config_name, ConfigFileType type)
|
||||
{
|
||||
expected_configuration_files[config_name].insert(type);
|
||||
if (type != ConfigFileType::RawData) config_file_paths.insert(getPolicyConfigPath(config_name, type));
|
||||
}
|
||||
|
||||
void
|
||||
ConfigComponent::Impl::registerExpectedConfiguration(unique_ptr<GenericConfig<true>> &&expected_config)
|
||||
{
|
||||
expected_configs.insert(move(expected_config));
|
||||
}
|
||||
|
||||
void
|
||||
ConfigComponent::Impl::registerExpectedResource(unique_ptr<GenericConfig<false>> &&expected_config)
|
||||
{
|
||||
expected_resources.insert(move(expected_config));
|
||||
}
|
||||
|
||||
void
|
||||
ConfigComponent::Impl::registerExpectedSetting(unique_ptr<GenericConfig<false>> &&expected_config)
|
||||
{
|
||||
expected_settings.insert(move(expected_config));
|
||||
}
|
||||
|
||||
bool
|
||||
ConfigComponent::Impl::loadConfiguration(istream &stream)
|
||||
{
|
||||
vector<shared_ptr<JSONInputArchive>> archive;
|
||||
try {
|
||||
archive.emplace_back(make_shared<JSONInputArchive>(stream));
|
||||
} catch (const cereal::Exception &e) {
|
||||
dbgError(D_CONFIG) << "Failed to load stream: " << e.what();
|
||||
return false;
|
||||
}
|
||||
return loadConfiguration(archive, false);
|
||||
}
|
||||
|
||||
bool
|
||||
ConfigComponent::Impl::loadConfiguration(const vector<string> &flags)
|
||||
{
|
||||
for (auto &flag : flags) {
|
||||
if (flag.substr(0, 2) == "--") {
|
||||
auto equal_place = flag.find_first_of('=');
|
||||
if (equal_place == string::npos) continue;
|
||||
dbgDebug(D_CONFIG)
|
||||
<< "Adding "
|
||||
<< flag.substr(2, equal_place - 2)
|
||||
<< "='"
|
||||
<< flag.substr(equal_place +1)
|
||||
<< "'";
|
||||
new_config_flags.emplace(flag.substr(2, equal_place - 2), flag.substr(equal_place +1));
|
||||
} else {
|
||||
dbgInfo(D_CONFIG) << "ignoring an illegal configuration argument. Argument: " << flag;
|
||||
}
|
||||
}
|
||||
reloadFileSystemPaths();
|
||||
|
||||
auto &alternative_conf_path = getConfigurationFlag("configDirectoryPath");
|
||||
if (alternative_conf_path != "") {
|
||||
config_directory_path = alternative_conf_path;
|
||||
dbgTrace(D_CONFIG) << "File system path reloaded from configuration flag: " << config_directory_path;
|
||||
}
|
||||
|
||||
auto res = reloadConfiguration("", false, 0) == I_Config::AsyncLoadConfigStatus::Success;
|
||||
|
||||
if (res && !new_config_flags.empty()) {
|
||||
config_flags = move(new_config_flags);
|
||||
} else {
|
||||
new_config_flags.clear();
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
I_Config::AsyncLoadConfigStatus
|
||||
ConfigComponent::Impl::reloadConfiguration(const string &version, bool is_async, uint id)
|
||||
{
|
||||
if (is_continuous_report) {
|
||||
dbgWarning(D_CONFIG) << "Cannot start another continuous reload while another is running.";
|
||||
return AsyncLoadConfigStatus::InProgress;
|
||||
}
|
||||
|
||||
if (!is_async) {
|
||||
bool res = reloadConfigurationImpl(version, false);
|
||||
return res ? I_Config::AsyncLoadConfigStatus::Success : I_Config::AsyncLoadConfigStatus::Error;
|
||||
}
|
||||
|
||||
is_continuous_report = true;
|
||||
|
||||
auto mainloop = Singleton::Consume<I_MainLoop>::by<ConfigComponent>();
|
||||
|
||||
mainloop->addOneTimeRoutine(
|
||||
I_MainLoop::RoutineType::System,
|
||||
[=] () { reloadConfigurationContinuesWrapper(version, id); },
|
||||
"A-Synchronize reload configuraion"
|
||||
);
|
||||
|
||||
return AsyncLoadConfigStatus::Success;
|
||||
}
|
||||
|
||||
void
|
||||
ConfigComponent::Impl::registerConfigPrepareCb(ConfigCb cb)
|
||||
{
|
||||
configuration_prepare_cbs.push_back(cb);
|
||||
}
|
||||
|
||||
void
|
||||
ConfigComponent::Impl::registerConfigLoadCb(ConfigCb cb)
|
||||
{
|
||||
configuration_commit_cbs.push_back(cb);
|
||||
}
|
||||
|
||||
void
|
||||
ConfigComponent::Impl::registerConfigAbortCb(ConfigCb cb)
|
||||
{
|
||||
configuration_abort_cbs.push_back(cb);
|
||||
}
|
||||
|
||||
void
|
||||
ConfigComponent::Impl::clearOldTenants()
|
||||
{
|
||||
for (
|
||||
auto iter = configuration_nodes.begin();
|
||||
iter != configuration_nodes.end();
|
||||
!isTenantActive(iter->first) ? iter = configuration_nodes.erase(iter) : ++iter
|
||||
);
|
||||
|
||||
for (
|
||||
auto iter = settings_nodes.begin();
|
||||
iter != settings_nodes.end();
|
||||
!isTenantActive(iter->first) ? iter = settings_nodes.erase(iter) : ++iter
|
||||
);
|
||||
}
|
||||
|
||||
bool
|
||||
ConfigComponent::Impl::isTenantActive(const string &name) const
|
||||
{
|
||||
return name == default_tenant_id || tenant_mananger->isTenantActive(name);
|
||||
}
|
||||
|
||||
void
|
||||
ConfigComponent::Impl::periodicRegistrationRefresh()
|
||||
{
|
||||
I_Environment *environment = Singleton::Consume<I_Environment>::by<ConfigComponent>();
|
||||
I_MainLoop *mainloop = Singleton::Consume<I_MainLoop>::by<ConfigComponent>();
|
||||
|
||||
while (true) {
|
||||
auto env_listening_port = environment->get<int>("Listening Port");
|
||||
|
||||
if (!env_listening_port.ok()) {
|
||||
dbgTrace(D_CONFIG)
|
||||
<< "Internal rest server listening port is not yet set."
|
||||
<< " Setting retry attempt to 500 milliseconds from now";
|
||||
mainloop->yield(chrono::milliseconds(500));
|
||||
} else if (!sendOrchestatorConfMsg(env_listening_port.unpack())) {
|
||||
mainloop->yield(chrono::milliseconds(500));
|
||||
} else {
|
||||
uint next_iteration_in_sec = getConfigurationWithDefault<uint>(
|
||||
600,
|
||||
"Config Component",
|
||||
"Refresh config update registration time interval"
|
||||
);
|
||||
mainloop->yield(chrono::seconds(next_iteration_in_sec));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
ConfigComponent::Impl::loadConfiguration(vector<shared_ptr<JSONInputArchive>> &file_archives, bool is_async)
|
||||
{
|
||||
auto mainloop = is_async ? Singleton::Consume<I_MainLoop>::by<ConfigComponent>() : nullptr;
|
||||
|
||||
for (auto &cb : configuration_prepare_cbs) {
|
||||
cb();
|
||||
}
|
||||
|
||||
try {
|
||||
for (auto &archive : file_archives) {
|
||||
for (auto &resource : expected_resources) {
|
||||
auto loaded = resource->loadConfiguration(*archive);
|
||||
if (loaded.ok()) new_resource_nodes[resource->getPath()] = loaded;
|
||||
if (is_async) mainloop->yield();
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &archive : file_archives) {
|
||||
string curr_tennat = default_tenant_id;
|
||||
try {
|
||||
(*archive)(cereal::make_nvp("tenantID", curr_tennat));
|
||||
} catch (cereal::Exception &e) {
|
||||
}
|
||||
|
||||
for (auto &config : expected_configs) {
|
||||
auto loaded = config->loadConfiguration(*archive);
|
||||
if (!loaded.empty()) new_configuration_nodes[curr_tennat][config->getPath()] = move(loaded);
|
||||
if (is_async) mainloop->yield();
|
||||
}
|
||||
for (auto &setting : expected_settings) {
|
||||
auto loaded = setting->loadConfiguration(*archive);
|
||||
if (loaded.ok()) new_settings_nodes[curr_tennat][setting->getPath()] = move(loaded);
|
||||
if (is_async) mainloop->yield();
|
||||
}
|
||||
}
|
||||
} catch (const cereal::Exception &e) {
|
||||
return commitFailure(e.what());
|
||||
} catch (const Config::ConfigException &e) {
|
||||
return commitFailure(e.getError());
|
||||
} catch (const EnvironmentHelper::EvaluatorParseError &e) {
|
||||
return commitFailure(e.getError());
|
||||
}
|
||||
|
||||
return commitSuccess();
|
||||
}
|
||||
|
||||
bool
|
||||
ConfigComponent::Impl::commitSuccess()
|
||||
{
|
||||
new_resource_nodes.clear();
|
||||
configuration_nodes = move(new_configuration_nodes);
|
||||
settings_nodes = move(new_settings_nodes);
|
||||
|
||||
AgentProfileSettings profile_agent_settings = getSettingWithDefault<AgentProfileSettings>(
|
||||
AgentProfileSettings::default_profile_settings,
|
||||
"agentSettings"
|
||||
);
|
||||
|
||||
AgentProfileSettings general_agent_settings = getSettingWithDefault<AgentProfileSettings>(
|
||||
AgentProfileSettings::default_profile_settings,
|
||||
"generalAgentSettings"
|
||||
);
|
||||
|
||||
auto tmp_general_settings = general_agent_settings.getSettings();
|
||||
|
||||
for (const pair<string, string> &profile_setting : profile_agent_settings.getSettings()) {
|
||||
tmp_general_settings.insert(profile_setting);
|
||||
}
|
||||
|
||||
profile_settings = tmp_general_settings;
|
||||
reloadFileSystemPaths();
|
||||
|
||||
for (auto &cb : configuration_commit_cbs) {
|
||||
cb();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ConfigComponent::Impl::commitFailure(const string &error)
|
||||
{
|
||||
dbgError(D_CONFIG) << error;
|
||||
new_resource_nodes.clear();
|
||||
new_configuration_nodes.clear();
|
||||
new_settings_nodes.clear();
|
||||
for (auto &cb : configuration_abort_cbs) {
|
||||
cb();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
ConfigComponent::Impl::reloadConfigurationImpl(const string &version, bool is_async)
|
||||
{
|
||||
auto env = Singleton::Consume<I_Environment>::by<ConfigComponent>();
|
||||
env->registerValue<string>("New Policy Version", version);
|
||||
auto cleanup = make_scope_exit([env] () { env->unregisterKey<string>("New Policy Version"); } );
|
||||
|
||||
map<string, shared_ptr<ifstream>> files;
|
||||
for (const auto &path : config_file_paths) {
|
||||
auto fullpath = config_directory_path + path;
|
||||
files.emplace(fullpath, make_shared<ifstream>(fullpath));
|
||||
}
|
||||
|
||||
const vector<string> &active_tenants = tenant_mananger ? tenant_mananger->fetchActiveTenants() : vector<string>();
|
||||
for (const auto &config_file : expected_configuration_files) {
|
||||
for (const auto &type : config_file.second) {
|
||||
if (type == ConfigFileType::RawData) continue;
|
||||
auto global_path = getPolicyConfigPath(config_file.first, type);
|
||||
if (files.find(global_path) == files.end()) {
|
||||
files.emplace(global_path, make_shared<ifstream>(global_path));
|
||||
}
|
||||
|
||||
for (auto &tenant : active_tenants) {
|
||||
auto tenant_path = getPolicyConfigPath(config_file.first, type, tenant);
|
||||
files.emplace(tenant_path, make_shared<ifstream>(tenant_path));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vector<shared_ptr<JSONInputArchive>> archives;
|
||||
for (const auto &file : files) {
|
||||
if (file.second->is_open()) {
|
||||
archives.push_back(make_shared<JSONInputArchive>(*file.second));
|
||||
} else {
|
||||
dbgDebug(D_CONFIG) << "Could not open configuration file. Path: " << file.first;
|
||||
}
|
||||
}
|
||||
|
||||
bool res = loadConfiguration(archives, is_async);
|
||||
if (res) env->registerValue<string>("Current Policy Version", version);
|
||||
return res;
|
||||
}
|
||||
|
||||
void
|
||||
ConfigComponent::Impl::reloadConfigurationContinuesWrapper(const string &version, uint id)
|
||||
{
|
||||
auto mainloop = Singleton::Consume<I_MainLoop>::by<ConfigComponent>();
|
||||
|
||||
LoadNewConfigurationStatus in_progress(id, false, false);
|
||||
auto routine_id = mainloop->addRecurringRoutine(
|
||||
I_MainLoop::RoutineType::Timer,
|
||||
std::chrono::seconds(30),
|
||||
[=] () { sendOrchestatorReloadStatusMsg(in_progress); },
|
||||
"A-Synchronize reload configuraion monitoring"
|
||||
);
|
||||
|
||||
bool res = reloadConfigurationImpl(version, true);
|
||||
|
||||
mainloop->stop(routine_id);
|
||||
LoadNewConfigurationStatus finished(id, !res, true);
|
||||
if (!res) finished.setError("Failed to reload configuration");
|
||||
sendOrchestatorReloadStatusMsg(finished);
|
||||
|
||||
is_continuous_report = false;
|
||||
}
|
||||
|
||||
ConfigComponent::ConfigComponent() : Component("ConfigComponent"), pimpl(make_unique<Impl>()) {}
|
||||
ConfigComponent::~ConfigComponent() {}
|
||||
|
||||
void
|
||||
ConfigComponent::preload()
|
||||
{
|
||||
registerExpectedConfiguration<string>("Config Component", "configuration path");
|
||||
registerExpectedConfiguration<uint>("Config Component", "Refresh config update registration time interval");
|
||||
registerExpectedResource<bool>("Config Component", "Config Load Test");
|
||||
registerExpectedSetting<AgentProfileSettings>("agentSettings");
|
||||
pimpl->preload();
|
||||
}
|
||||
|
||||
void
|
||||
ConfigComponent::init()
|
||||
{
|
||||
if (Singleton::exists<I_RestApi>()) {
|
||||
auto rest = Singleton::Consume<I_RestApi>::by<ConfigComponent>();
|
||||
rest->addRestCall<LoadNewConfiguration>(RestAction::SET, "new-configuration");
|
||||
}
|
||||
pimpl->init();
|
||||
}
|
89
core/config/config_globals.cc
Normal file
89
core/config/config_globals.cc
Normal file
@@ -0,0 +1,89 @@
|
||||
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "config.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace Config;
|
||||
|
||||
void
|
||||
reportConfigurationError(const string &err)
|
||||
{
|
||||
throw ConfigException(err);
|
||||
}
|
||||
|
||||
ostream &
|
||||
operator<<(ostream &os, const Errors &err)
|
||||
{
|
||||
switch (err) {
|
||||
case Config::Errors::MISSING_TAG: return os << "MISSING_TAG";
|
||||
case Config::Errors::MISSING_CONTEXT: return os << "MISSING_CONTEXT";
|
||||
case Config::Errors::BAD_NODE: return os << "BAD_NODE";
|
||||
}
|
||||
return os << "Unknown error";
|
||||
}
|
||||
|
||||
void
|
||||
registerConfigPrepareCb(ConfigCb cb)
|
||||
{
|
||||
Singleton::Consume<I_Config>::from<MockConfigProvider>()->registerConfigPrepareCb(cb);
|
||||
}
|
||||
|
||||
void
|
||||
registerConfigLoadCb(ConfigCb cb)
|
||||
{
|
||||
Singleton::Consume<I_Config>::from<MockConfigProvider>()->registerConfigLoadCb(cb);
|
||||
}
|
||||
|
||||
void
|
||||
registerConfigAbortCb(ConfigCb cb)
|
||||
{
|
||||
Singleton::Consume<I_Config>::from<MockConfigProvider>()->registerConfigAbortCb(cb);
|
||||
}
|
||||
|
||||
bool
|
||||
reloadConfiguration(const std::string &version)
|
||||
{
|
||||
auto res = Singleton::Consume<I_Config>::from<MockConfigProvider>()->reloadConfiguration(version, false, 0);
|
||||
return res == I_Config::AsyncLoadConfigStatus::Success;
|
||||
}
|
||||
|
||||
string
|
||||
getConfigurationFlag(const string &flag)
|
||||
{
|
||||
return Singleton::Consume<I_Config>::from<MockConfigProvider>()->getConfigurationFlag(flag);
|
||||
}
|
||||
|
||||
const string &
|
||||
getFilesystemPathConfig()
|
||||
{
|
||||
return Singleton::Consume<I_Config>::from<MockConfigProvider>()->getFilesystemPathConfig();
|
||||
}
|
||||
|
||||
const string &
|
||||
getLogFilesPathConfig()
|
||||
{
|
||||
return Singleton::Consume<I_Config>::from<MockConfigProvider>()->getLogFilesPathConfig();
|
||||
}
|
||||
|
||||
string
|
||||
getPolicyConfigPath(const string &name, ConfigFileType type, const string &tenant)
|
||||
{
|
||||
return Singleton::Consume<I_Config>::from<MockConfigProvider>()->getPolicyConfigPath(name, type, tenant);
|
||||
}
|
||||
|
||||
void
|
||||
registerExpectedConfigFile(const string &config_name, ConfigFileType type)
|
||||
{
|
||||
Singleton::Consume<I_Config>::from<MockConfigProvider>()->registerExpectedConfigFile(config_name, type);
|
||||
}
|
79
core/config/config_specific.cc
Normal file
79
core/config/config_specific.cc
Normal file
@@ -0,0 +1,79 @@
|
||||
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "config.h"
|
||||
|
||||
using namespace cereal;
|
||||
using namespace std;
|
||||
|
||||
template<>
|
||||
void
|
||||
Config::ConfigLoader<bool>::readValue(JSONInputArchive &ar)
|
||||
{
|
||||
ar(make_nvp("value", value));
|
||||
}
|
||||
|
||||
template<>
|
||||
void
|
||||
Config::ConfigLoader<int>::readValue(JSONInputArchive &ar)
|
||||
{
|
||||
ar(make_nvp("value", value));
|
||||
}
|
||||
|
||||
template<>
|
||||
void
|
||||
Config::ConfigLoader<uint>::readValue(JSONInputArchive &ar)
|
||||
{
|
||||
ar(make_nvp("value", value));
|
||||
}
|
||||
|
||||
template<>
|
||||
void
|
||||
Config::ConfigLoader<string>::readValue(JSONInputArchive &ar)
|
||||
{
|
||||
ar(make_nvp("value", value));
|
||||
}
|
||||
|
||||
template<>
|
||||
bool
|
||||
Config::loadProfileSetting<bool>(const string &raw_value)
|
||||
{
|
||||
if (raw_value == "true") {
|
||||
return true;
|
||||
} else if (raw_value == "false") {
|
||||
return false;
|
||||
} else {
|
||||
throw Exception("Illegal Value");
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
int
|
||||
Config::loadProfileSetting<int>(const string &raw_value)
|
||||
{
|
||||
return stoi(raw_value);
|
||||
}
|
||||
|
||||
template<>
|
||||
uint
|
||||
Config::loadProfileSetting<uint>(const string &raw_value)
|
||||
{
|
||||
return stoul(raw_value);
|
||||
}
|
||||
|
||||
template<>
|
||||
string
|
||||
Config::loadProfileSetting<string>(const string &raw_value)
|
||||
{
|
||||
return raw_value;
|
||||
}
|
63
core/config/include/profile_settings.h
Executable file
63
core/config/include/profile_settings.h
Executable file
@@ -0,0 +1,63 @@
|
||||
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef __PROFILE_SETTINGS_H__
|
||||
#define __PROFILE_SETTINGS_H__
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
class AgentProfileSettings
|
||||
{
|
||||
public:
|
||||
void
|
||||
load(cereal::JSONInputArchive &ar)
|
||||
{
|
||||
std::vector<SingleSetting> settings;
|
||||
cereal::load(ar, settings);
|
||||
|
||||
for (const SingleSetting &setting : settings) {
|
||||
std::pair<std::string, std::string> single_setting = setting.getSetting();
|
||||
profile_settings[boost::algorithm::trim_copy(single_setting.first)] =
|
||||
boost::algorithm::trim_copy(single_setting.second);
|
||||
}
|
||||
}
|
||||
|
||||
const std::map<std::string, std::string> & getSettings() const { return profile_settings; }
|
||||
static AgentProfileSettings default_profile_settings;
|
||||
|
||||
private:
|
||||
class SingleSetting
|
||||
{
|
||||
public:
|
||||
void
|
||||
load(cereal::JSONInputArchive &ar)
|
||||
{
|
||||
ar(
|
||||
cereal::make_nvp("key", key),
|
||||
cereal::make_nvp("value", value)
|
||||
);
|
||||
}
|
||||
|
||||
std::pair<std::string, std::string> getSetting() const { return std::make_pair(key, value); }
|
||||
|
||||
private:
|
||||
std::string key;
|
||||
std::string value;
|
||||
};
|
||||
std::map<std::string, std::string> profile_settings;
|
||||
};
|
||||
|
||||
#endif // __PROFILE_SETTINGS_H__
|
Reference in New Issue
Block a user