diff --git a/README.md b/README.md index 0c61321..217d408 100644 --- a/README.md +++ b/README.md @@ -104,12 +104,13 @@ Before compiling the services, you'll need to ensure the latest development vers * GTest * GMock * cURL +* Hiredis An example of installing the packages on Alpine: ```bash $ apk update - $ apk add boost-dev openssl-dev pcre2-dev libxml2-dev gtest-dev curl-dev + $ apk add boost-dev openssl-dev pcre2-dev libxml2-dev gtest-dev curl-dev hiredis-dev ``` ## Compiling and packaging the agent code diff --git a/components/generic_rulebase/evaluators/http_transaction_data_eval.cc b/components/generic_rulebase/evaluators/http_transaction_data_eval.cc index fa90b6a..d6560fb 100755 --- a/components/generic_rulebase/evaluators/http_transaction_data_eval.cc +++ b/components/generic_rulebase/evaluators/http_transaction_data_eval.cc @@ -20,6 +20,9 @@ #include "environment/evaluator_templates.h" #include "i_environment.h" #include "singleton.h" +#include "debug.h" + +USE_DEBUG_FLAG(D_RULEBASE_CONFIG); using namespace std; using namespace EnvironmentHelper; @@ -55,6 +58,51 @@ EqualHost::evalVariable() const return lower_host_ctx == lower_host; } +WildcardHost::WildcardHost(const vector ¶ms) +{ + if (params.size() != 1) reportWrongNumberOfParams("WildcardHost", params.size(), 1, 1); + host = params[0]; +} + +Maybe +WildcardHost::evalVariable() const +{ + I_Environment *env = Singleton::Consume::by(); + auto host_ctx = env->get(HttpTransactionData::host_name_ctx); + + if (!host_ctx.ok()) + { + return false; + } + + string lower_host_ctx = host_ctx.unpack(); + transform(lower_host_ctx.begin(), lower_host_ctx.end(), lower_host_ctx.begin(), ::tolower); + + dbgTrace(D_RULEBASE_CONFIG) << "found host in current context: " << lower_host_ctx; + + size_t pos = lower_host_ctx.find_first_of("."); + if (pos == string::npos) { + return false; + } + + lower_host_ctx = "*" + lower_host_ctx.substr(pos, lower_host_ctx.length()); + + string lower_host = host; + transform(lower_host.begin(), lower_host.end(), lower_host.begin(), ::tolower); + + dbgTrace(D_RULEBASE_CONFIG) + << "trying to match host context with its corresponding wildcard address: " + << lower_host_ctx + << ". Matcher host: " + << lower_host; + + if (lower_host_ctx == lower_host) return true; + pos = lower_host_ctx.find_last_of(':'); + if (pos == string::npos) return false; + lower_host_ctx = string(lower_host_ctx.data(), pos); + return lower_host_ctx == lower_host; +} + EqualListeningIP::EqualListeningIP(const vector ¶ms) { if (params.size() != 1) reportWrongNumberOfParams("EqualListeningIP", params.size(), 1, 1); diff --git a/components/generic_rulebase/generic_rulebase.cc b/components/generic_rulebase/generic_rulebase.cc index 4f115c3..d4fd8f9 100755 --- a/components/generic_rulebase/generic_rulebase.cc +++ b/components/generic_rulebase/generic_rulebase.cc @@ -75,6 +75,7 @@ GenericRulebase::Impl::preload() addMatcher(); addMatcher(); addMatcher(); + addMatcher(); addMatcher(); addMatcher(); addMatcher(); diff --git a/components/include/generic_rulebase/evaluators/http_transaction_data_eval.h b/components/include/generic_rulebase/evaluators/http_transaction_data_eval.h index 8603bc5..e9fb041 100755 --- a/components/include/generic_rulebase/evaluators/http_transaction_data_eval.h +++ b/components/include/generic_rulebase/evaluators/http_transaction_data_eval.h @@ -32,6 +32,19 @@ private: std::string host; }; +class WildcardHost : public EnvironmentEvaluator, Singleton::Consume +{ +public: + WildcardHost(const std::vector ¶ms); + + static std::string getName() { return "WildcardHost"; } + + Maybe evalVariable() const override; + +private: + std::string host; +}; + class EqualListeningIP : public EnvironmentEvaluator, Singleton::Consume { public: diff --git a/components/include/i_local_policy_mgmt_gen.h b/components/include/i_local_policy_mgmt_gen.h index a4ae7ed..d1874ad 100755 --- a/components/include/i_local_policy_mgmt_gen.h +++ b/components/include/i_local_policy_mgmt_gen.h @@ -14,13 +14,15 @@ #ifndef __I_LOCAL_POLICY_MGMT_GEN_H__ #define __I_LOCAL_POLICY_MGMT_GEN_H__ +#include "i_env_details.h" + class I_LocalPolicyMgmtGen { public: - virtual std::string parsePolicy(const std::string &policy_version) = 0; - virtual const std::string & getAgentPolicyPath(void) const = 0; - virtual const std::string & getLocalPolicyPath(void) const = 0; - virtual void setPolicyPath(const std::string &new_local_policy_path) = 0; + virtual std::string generateAppSecLocalPolicy( + EnvType env_type, + const std::string &policy_version, + const std::string &local_policy_path) = 0; protected: ~I_LocalPolicyMgmtGen() {} diff --git a/components/include/i_orchestration_status.h b/components/include/i_orchestration_status.h index 4305c55..d99bb0c 100755 --- a/components/include/i_orchestration_status.h +++ b/components/include/i_orchestration_status.h @@ -34,6 +34,7 @@ public: virtual const std::string & getUpdateTime() const = 0; virtual const std::string & getLastManifestUpdate() const = 0; virtual const std::string & getPolicyVersion() const = 0; + virtual const std::string & getWaapModelVersion() const = 0; virtual const std::string & getLastPolicyUpdate() const = 0; virtual const std::string & getLastSettingsUpdate() const = 0; virtual const std::string & getUpgradeMode() const = 0; diff --git a/components/include/i_orchestration_tools.h b/components/include/i_orchestration_tools.h index 18da6c9..692d513 100755 --- a/components/include/i_orchestration_tools.h +++ b/components/include/i_orchestration_tools.h @@ -106,8 +106,9 @@ public: const std::string &profile_id = "") const = 0; virtual bool isNonEmptyFile(const std::string &path) const = 0; + virtual std::shared_ptr fileStreamWrapper(const std::string &path) const = 0; virtual Maybe readFile(const std::string &path) const = 0; - virtual bool writeFile(const std::string &text, const std::string &path) const = 0; + virtual bool writeFile(const std::string &text, const std::string &path, bool append_mode = false) const = 0; virtual bool removeFile(const std::string &path) const = 0; virtual bool removeDirectory(const std::string &path, bool delete_content) const = 0; virtual void deleteVirtualTenantProfileFiles( @@ -116,6 +117,7 @@ public: const std::string &conf_path) const = 0; virtual bool copyFile(const std::string &src_path, const std::string &dst_path) const = 0; virtual bool doesFileExist(const std::string &file_path) const = 0; + virtual void getClusterId() const = 0; virtual void fillKeyInJson( const std::string &filename, const std::string &_key, diff --git a/components/include/orchestration_comp.h b/components/include/orchestration_comp.h index 2ab061d..5a02dcd 100755 --- a/components/include/orchestration_comp.h +++ b/components/include/orchestration_comp.h @@ -31,6 +31,7 @@ #include "i_environment.h" #include "i_tenant_manager.h" #include "i_package_handler.h" +#include "i_env_details.h" #include "component.h" class OrchestrationComp @@ -52,7 +53,8 @@ class OrchestrationComp Singleton::Consume, Singleton::Consume, Singleton::Consume, - Singleton::Consume + Singleton::Consume, + Singleton::Consume { public: OrchestrationComp(); diff --git a/components/include/orchestration_status.h b/components/include/orchestration_status.h index 84879a4..b9adb63 100755 --- a/components/include/orchestration_status.h +++ b/components/include/orchestration_status.h @@ -24,6 +24,7 @@ #include "i_time_get.h" #include "i_mainloop.h" #include "i_agent_details.h" +#include "i_details_resolver.h" #include "customized_cereal_map.h" class OrchestrationStatus @@ -32,6 +33,7 @@ class OrchestrationStatus Singleton::Provide, Singleton::Consume, Singleton::Consume, + Singleton::Consume, Singleton::Consume, Singleton::Consume { diff --git a/components/include/orchestration_tools.h b/components/include/orchestration_tools.h index caa851c..c85eed3 100755 --- a/components/include/orchestration_tools.h +++ b/components/include/orchestration_tools.h @@ -20,13 +20,23 @@ #include "i_shell_cmd.h" #include "i_tenant_manager.h" #include "component.h" +#include "i_env_details.h" +#include "i_messaging.h" +#include "i_environment.h" +#include "i_agent_details.h" +#include "i_mainloop.h" class OrchestrationTools : public Component, Singleton::Provide, Singleton::Consume, - Singleton::Consume + Singleton::Consume, + Singleton::Consume, + Singleton::Consume, + Singleton::Consume, + Singleton::Consume, + Singleton::Consume { public: OrchestrationTools(); diff --git a/components/include/orchestrator/rest_api/orchestration_check_update.h b/components/include/orchestrator/rest_api/orchestration_check_update.h index 2304ecd..26bf7d5 100644 --- a/components/include/orchestrator/rest_api/orchestration_check_update.h +++ b/components/include/orchestrator/rest_api/orchestration_check_update.h @@ -111,6 +111,26 @@ public: public: UpgradeSchedule() = default; + UpgradeSchedule(const UpgradeSchedule &other) + { + mode = other.mode; + time = other.time; + duration_hours = other.duration_hours; + days = other.days; + } + + UpgradeSchedule & + operator=(const UpgradeSchedule &other) + { + if (this != &other) { + mode = other.mode; + time = other.time; + duration_hours = other.duration_hours; + days = other.days; + } + return *this; + } + void init(const std::string &_upgrade_mode) { mode = _upgrade_mode; } void @@ -142,6 +162,22 @@ public: C2S_LABEL_OPTIONAL_PARAM(std::vector, days, "upgradeDay"); }; + class LocalConfigurationSettings : public ClientRest + { + public: + LocalConfigurationSettings() = default; + + void + setUpgradeSchedule(const UpgradeSchedule &schedule) + { + upgrade_schedule.setActive(true); + upgrade_schedule.get() = schedule; + } + + private: + C2S_LABEL_OPTIONAL_PARAM(UpgradeSchedule, upgrade_schedule, "upgradeSchedule"); + }; + CheckUpdateRequest( const std::string &_manifest, const std::string &_policy, @@ -224,8 +260,10 @@ public: void setUpgradeFields(const std::string &_upgrade_mode) { - upgrade_schedule.setActive(true); - upgrade_schedule.get().init(_upgrade_mode); + UpgradeSchedule upgrade_schedule; + upgrade_schedule.init(_upgrade_mode); + local_configuration_settings.setActive(true); + local_configuration_settings.get().setUpgradeSchedule(upgrade_schedule); } void @@ -235,12 +273,14 @@ public: const uint &_upgrade_duration_hours, const std::vector &_upgrade_days) { - upgrade_schedule.setActive(true); + UpgradeSchedule upgrade_schedule; if (!_upgrade_days.empty()) { - upgrade_schedule.get().init(_upgrade_mode, _upgrade_time, _upgrade_duration_hours, _upgrade_days); - return; + upgrade_schedule.init(_upgrade_mode, _upgrade_time, _upgrade_duration_hours, _upgrade_days); + } else { + upgrade_schedule.init(_upgrade_mode, _upgrade_time, _upgrade_duration_hours); } - upgrade_schedule.get().init(_upgrade_mode, _upgrade_time, _upgrade_duration_hours); + local_configuration_settings.setActive(true); + local_configuration_settings.get().setUpgradeSchedule(upgrade_schedule); } private: @@ -297,7 +337,7 @@ private: C2S_LABEL_PARAM(std::string, checksum_type, "checksum-type"); C2S_LABEL_PARAM(std::string, policy_version, "policyVersion"); - C2S_LABEL_OPTIONAL_PARAM(UpgradeSchedule, upgrade_schedule, "upgradeSchedule"); + C2S_LABEL_OPTIONAL_PARAM(LocalConfigurationSettings, local_configuration_settings, "localConfigurationSettings"); S2C_LABEL_OPTIONAL_PARAM(VirtualConfig, in_virtual_policy, "virtualPolicy"); S2C_LABEL_OPTIONAL_PARAM(VirtualConfig, in_virtual_settings, "virtualSettings"); diff --git a/components/include/rate_limit.h b/components/include/rate_limit.h new file mode 100755 index 0000000..3c3e726 --- /dev/null +++ b/components/include/rate_limit.h @@ -0,0 +1,32 @@ +#ifndef __RATE_LIMIT_H_ +#define __RATE_LIMIT_H_ + +#include + +#include "component.h" +#include "singleton.h" +#include "i_mainloop.h" +#include "i_environment.h" + +class RateLimit + : + public Component, + Singleton::Consume, + Singleton::Consume, + Singleton::Consume +{ +public: + RateLimit(); + ~RateLimit(); + + void preload() override; + + void init() override; + void fini() override; + +private: + class Impl; + std::unique_ptr pimpl; +}; + +#endif // __RATE_LIMIT_H_ diff --git a/components/include/rate_limit_config.h b/components/include/rate_limit_config.h new file mode 100755 index 0000000..b464d3e --- /dev/null +++ b/components/include/rate_limit_config.h @@ -0,0 +1,142 @@ +#ifndef __RATE_LIMIT_CONFIG_H__ +#define __RATE_LIMIT_CONFIG_H__ + +#include +#include +#include +#include + +#include "debug.h" +#include "generic_rulebase/rulebase_config.h" +#include "generic_rulebase/triggers_config.h" +#include "generic_rulebase/evaluators/trigger_eval.h" + +USE_DEBUG_FLAG(D_REVERSE_PROXY); + +class RateLimitTrigger +{ +public: + void + load(cereal::JSONInputArchive &ar); + + const std::string & getTriggerId() const { return id; } + +private: + std::string id; +}; + +class RateLimitRule +{ +public: + void load(cereal::JSONInputArchive &ar); + void prepare(const std::string &asset_id, int zone_id); + + operator bool() const + { + if (uri.empty()) { + dbgTrace(D_REVERSE_PROXY) << "Recived empty URI in rate-limit rule"; + return false; + } + + if (uri.at(0) != '/') { + dbgWarning(D_REVERSE_PROXY) + << "Recived invalid rate-limit URI in rate-limit rule: " + << uri + << " rate-limit URI must start with /"; + return false; + } + + if (limit <= 0) { + dbgWarning(D_REVERSE_PROXY) + << "Recived invalid rate-limit limit in rate-limit rule: " + << limit + << " rate-limit rule limit must be positive"; + return false; + } + + return true; + } + + friend std::ostream & + operator<<(std::ostream &os, const RateLimitRule &rule) + { + os << "Uri: " << rule.uri << ", Rate scope: " << rule.scope << ", Limit: " << rule.limit; + + return os; + } + + int getRateLimit() const { return limit; } + const std::string & getRateLimitZone() const { return limit_req_zone_template_value; } + const std::string & getRateLimitReq() const { return limit_req_template_value; } + const std::string & getRateLimitUri() const { return uri; } + const std::string & getRateLimitScope() const { return scope; } + const LogTriggerConf & getRateLimitTrigger() const { return trigger; } + const std::vector & getRateLimitTriggers() const { return rate_limit_triggers; } + + bool isRootLocation() const; + + bool operator==(const RateLimitRule &rhs) { return uri == rhs.uri; } + bool operator<(const RateLimitRule &rhs) { return uri < rhs.uri; } + bool isExactMatch() const { return exact_match || (!uri.empty() && uri.back() != '/'); } + void setExactMatch() { exact_match = true; } + void appendSlash() { uri += '/'; } + +private: + std::string uri; + std::string scope; + std::string limit_req_template_value; + std::string limit_req_zone_template_value; + std::string cache_size = "5m"; + std::vector rate_limit_triggers; + LogTriggerConf trigger; + int limit; + bool exact_match = false; +}; + +class RateLimitConfig +{ +public: + void load(cereal::JSONInputArchive &ar); + void addSiblingRateLimitRule(RateLimitRule &rule); + void prepare(); + + const std::vector & getRateLimitRules() const { return rate_limit_rules; } + const std::string & getRateLimitMode() const { return mode; } + + const LogTriggerConf + getRateLimitTrigger(const std::string &nginx_uri) const + { + const RateLimitRule rule = findLongestMatchingRule(nginx_uri); + + std::set rate_limit_triggers_set; + for (const RateLimitTrigger &rate_limit_trigger : rule.getRateLimitTriggers()) { + dbgTrace(D_REVERSE_PROXY) + << "Adding trigger ID: " + << rate_limit_trigger.getTriggerId() + << " of rule URI: " + << rule.getRateLimitUri() + << " to the context set"; + rate_limit_triggers_set.insert(rate_limit_trigger.getTriggerId()); + } + + ScopedContext ctx; + ctx.registerValue>(TriggerMatcher::ctx_key, rate_limit_triggers_set); + return getConfigurationWithDefault(LogTriggerConf(), "rulebase", "log"); + } + + static void setIsActive(bool _is_active) { is_active |= _is_active; } + + static void resetIsActive() { is_active = false; } + + static bool isActive() { return is_active; } + +private: + const RateLimitRule + findLongestMatchingRule(const std::string &nginx_uri) const; + + static bool is_active; + std::string mode; + std::vector rate_limit_rules; +}; + +#endif // __RATE_LIMIT_CONFIG_H__ diff --git a/components/security_apps/CMakeLists.txt b/components/security_apps/CMakeLists.txt index ca6c3ad..bfde75c 100644 --- a/components/security_apps/CMakeLists.txt +++ b/components/security_apps/CMakeLists.txt @@ -1,4 +1,6 @@ add_subdirectory(ips) add_subdirectory(layer_7_access_control) +add_subdirectory(local_policy_mgmt_gen) add_subdirectory(orchestration) +add_subdirectory(rate_limit) add_subdirectory(waap) diff --git a/components/security_apps/layer_7_access_control/layer_7_access_control.cc b/components/security_apps/layer_7_access_control/layer_7_access_control.cc index e254a5e..299e8da 100644 --- a/components/security_apps/layer_7_access_control/layer_7_access_control.cc +++ b/components/security_apps/layer_7_access_control/layer_7_access_control.cc @@ -74,7 +74,7 @@ public: getCrowdsecEventId() const { if (!crowdsec_event_id) return genError("Empty ID"); - return LogField("externalVendorRecommendationId", crowdsec_event_id); + return LogField("externalVendorRecommendationId", to_string(crowdsec_event_id)); } bool isMalicious() const { return type == "ban"; } @@ -280,6 +280,8 @@ Layer7AccessControl::Impl::generateLog(const string &source_ip, const Intelligen << LogField("sourceIP", source_ip) << LogField("externalVendorName", "CrowdSec") << LogField("waapIncidentType", "CrowdSec") + << LogField("practiceSubType", "Web Access Control") + << LogField("practiceType", "Access Control") << ip_reputation.getCrowdsecEventId() << ip_reputation.getType() << ip_reputation.getOrigin() diff --git a/components/security_apps/layer_7_access_control/layer_7_access_control_ut/layer_7_access_control_ut.cc b/components/security_apps/layer_7_access_control/layer_7_access_control_ut/layer_7_access_control_ut.cc index 361438a..d722443 100644 --- a/components/security_apps/layer_7_access_control/layer_7_access_control_ut/layer_7_access_control_ut.cc +++ b/components/security_apps/layer_7_access_control/layer_7_access_control_ut/layer_7_access_control_ut.cc @@ -248,7 +248,7 @@ Layer7AccessControlTest::verifyReport( EXPECT_THAT(log, HasSubstr("\"destinationIP\": \"5.6.7.8\"")); EXPECT_THAT(log, HasSubstr("\"externalVendorName\": \"CrowdSec\"")); EXPECT_THAT(log, HasSubstr("\"waapIncidentType\": \"CrowdSec\"")); - EXPECT_THAT(log, HasSubstr("\"externalVendorRecommendationId\": 2253734")); + EXPECT_THAT(log, HasSubstr("\"externalVendorRecommendationId\": \"2253734\"")); EXPECT_THAT(log, HasSubstr("\"externalVendorRecommendedAction\": \"ban\"")); EXPECT_THAT(log, HasSubstr("\"externalVendorRecommendationOrigin\": \"cscli\"")); EXPECT_THAT(log, HasSubstr("\"externalVendorRecommendedAffectedScope\": \"1.2.3.4\"")); diff --git a/components/security_apps/local_policy_mgmt_gen/CMakeLists.txt b/components/security_apps/local_policy_mgmt_gen/CMakeLists.txt new file mode 100644 index 0000000..d76674b --- /dev/null +++ b/components/security_apps/local_policy_mgmt_gen/CMakeLists.txt @@ -0,0 +1,23 @@ +include_directories(include) +add_library(local_policy_mgmt_gen + appsec_practice_section.cc + exceptions_section.cc + ingress_data.cc + rules_config_section.cc + settings_section.cc + snort_section.cc + triggers_section.cc + trusted_sources_section.cc + policy_maker_utils.cc + k8s_policy_utils.cc + local_policy_mgmt_gen.cc + new_appsec_policy_crd_parser.cc + new_appsec_linux_policy.cc + new_custom_response.cc + new_trusted_sources.cc + new_log_trigger.cc + new_practice.cc + new_exceptions.cc + access_control_practice.cc + configmaps.cc +) diff --git a/components/security_apps/orchestration/local_policy_mgmt_gen/access_control_practice.cc b/components/security_apps/local_policy_mgmt_gen/access_control_practice.cc old mode 100644 new mode 100755 similarity index 100% rename from components/security_apps/orchestration/local_policy_mgmt_gen/access_control_practice.cc rename to components/security_apps/local_policy_mgmt_gen/access_control_practice.cc diff --git a/components/security_apps/orchestration/local_policy_mgmt_gen/appsec_practice_section.cc b/components/security_apps/local_policy_mgmt_gen/appsec_practice_section.cc old mode 100644 new mode 100755 similarity index 89% rename from components/security_apps/orchestration/local_policy_mgmt_gen/appsec_practice_section.cc rename to components/security_apps/local_policy_mgmt_gen/appsec_practice_section.cc index 043e168..4b90fca --- a/components/security_apps/orchestration/local_policy_mgmt_gen/appsec_practice_section.cc +++ b/components/security_apps/local_policy_mgmt_gen/appsec_practice_section.cc @@ -34,7 +34,7 @@ AppSecWebBotsURI::getURI() const return uri; } -std::vector +vector AppSecPracticeAntiBot::getIjectedUris() const { vector injected; @@ -44,7 +44,7 @@ AppSecPracticeAntiBot::getIjectedUris() const return injected; } -std::vector +vector AppSecPracticeAntiBot::getValidatedUris() const { vector validated; @@ -315,18 +315,74 @@ TriggersInWaapSection::save(cereal::JSONOutputArchive &out_ar) const ); } +ParsedMatch::ParsedMatch(const string &_operator, const string &_tag, const string &_value) + : + operator_type(_operator), + tag(_tag), + value(_value) +{ +} + +// LCOV_EXCL_START Reason: no test exist +ParsedMatch::ParsedMatch(const ExceptionMatch &exceptions) +{ + if (exceptions.getOperator() == "equals") { + operator_type = "basic"; + tag = exceptions.getKey(); + value = exceptions.getValue(); + } else { + operator_type = exceptions.getOperator(); + } + for (const ExceptionMatch &exception_match : exceptions.getMatch()) { + parsed_match.push_back(ParsedMatch(exception_match)); + } +} +// LCOV_EXCL_STOP + +void +ParsedMatch::save(cereal::JSONOutputArchive &out_ar) const +{ + if (parsed_match.size() > 0) { + out_ar(cereal::make_nvp("operator", operator_type)); + int i = 0; + for (const ParsedMatch &operand : parsed_match) { + i++; + out_ar(cereal::make_nvp("operand" + to_string(i), operand)); + } + } else { + out_ar( + cereal::make_nvp("operator", operator_type), + cereal::make_nvp("tag", tag), + cereal::make_nvp("value", value) + ); + } +} + AppSecOverride::AppSecOverride(const SourcesIdentifiers &parsed_trusted_sources) { string source_ident = parsed_trusted_sources.getSourceIdent(); map behavior = {{"httpSourceId", source_ident}}; parsed_behavior.push_back(behavior); - parsed_match = {{"operator", "BASIC"}, {"tag", "sourceip"}, {"value", "0.0.0.0/0"}}; + parsed_match = ParsedMatch("BASIC", "sourceip", "0.0.0.0/0"); } +// LCOV_EXCL_START Reason: no test exist +AppSecOverride::AppSecOverride(const InnerException &parsed_exceptions) + : + id(parsed_exceptions.getBehaviorId()), + parsed_match(parsed_exceptions.getMatch()) +{ + map behavior = {{parsed_exceptions.getBehaviorKey(), parsed_exceptions.getBehaviorValue()}}; + parsed_behavior.push_back(behavior); +} +// LCOV_EXCL_STOP + void AppSecOverride::save(cereal::JSONOutputArchive &out_ar) const { - string parameter_type = "TrustedSource"; + if (!id.empty()) { + out_ar(cereal::make_nvp("id", id)); + } out_ar( cereal::make_nvp("parsedBehavior", parsed_behavior), cereal::make_nvp("parsedMatch", parsed_match) @@ -355,7 +411,8 @@ WebAppSection::WebAppSection( const AppSecPracticeSpec &parsed_appsec_spec, const LogTriggerSection &parsed_log_trigger, const string &default_mode, - const AppSecTrustedSources &parsed_trusted_sources) + const AppSecTrustedSources &parsed_trusted_sources, + const vector &parsed_exceptions) : application_urls(_application_urls), asset_id(_asset_id), @@ -382,19 +439,23 @@ WebAppSection::WebAppSection( for (const SourcesIdentifiers &source_ident : parsed_trusted_sources.getSourcesIdentifiers()) { overrides.push_back(AppSecOverride(source_ident)); } + + for (const InnerException &exception : parsed_exceptions) { + overrides.push_back(AppSecOverride(exception)); + } } WebAppSection::WebAppSection( - const std::string &_application_urls, - const std::string &_asset_id, - const std::string &_asset_name, - const std::string &_rule_id, - const std::string &_rule_name, - const std::string &_practice_id, - const std::string &_practice_name, + const string &_application_urls, + const string &_asset_id, + const string &_asset_name, + const string &_rule_id, + const string &_rule_name, + const string &_practice_id, + const string &_practice_name, const string &_context, - const std::string &_web_attack_mitigation_severity, - const std::string &_web_attack_mitigation_mode, + const string &_web_attack_mitigation_severity, + const string &_web_attack_mitigation_mode, const PracticeAdvancedConfig &_practice_advanced_config, const AppsecPracticeAntiBotSection &_anti_bots, const LogTriggerSection &parsed_log_trigger, @@ -611,7 +672,7 @@ AppsecPolicySpec::getSpecificRules() const } bool -AppsecPolicySpec::isAssetHostExist(const std::string &full_url) const +AppsecPolicySpec::isAssetHostExist(const string &full_url) const { for (const ParsedRule &rule : specific_rules) { if (rule.getHost() == full_url) return true; @@ -633,7 +694,7 @@ AppsecLinuxPolicy::serialize(cereal::JSONInputArchive &archive_in) parseAppsecJSONKey>("practices", practices, archive_in); parseAppsecJSONKey>("log-triggers", log_triggers, archive_in); parseAppsecJSONKey>("custom-responses", custom_responses, archive_in); - parseAppsecJSONKey>("exceptions", exceptions, archive_in); + parseAppsecJSONKey>("exceptions", exceptions, archive_in); parseAppsecJSONKey>("trusted-sources", trusted_sources, archive_in); parseAppsecJSONKey>( "source-identifiers", @@ -666,8 +727,8 @@ AppsecLinuxPolicy::getAppSecCustomResponseSpecs() const return custom_responses; } -const vector & -AppsecLinuxPolicy::getAppsecExceptionSpecs() const +const vector & +AppsecLinuxPolicy::getAppsecExceptions() const { return exceptions; } diff --git a/components/security_apps/local_policy_mgmt_gen/configmaps.cc b/components/security_apps/local_policy_mgmt_gen/configmaps.cc new file mode 100755 index 0000000..33d4437 --- /dev/null +++ b/components/security_apps/local_policy_mgmt_gen/configmaps.cc @@ -0,0 +1,58 @@ +// 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 "configmaps.h" + +using namespace std; + +USE_DEBUG_FLAG(D_LOCAL_POLICY); + +// LCOV_EXCL_START Reason: no test exist +bool +ConfigMaps::loadJson(const std::string &json) +{ + string modified_json = json; + modified_json.pop_back(); + stringstream in; + in.str(modified_json); + dbgTrace(D_LOCAL_POLICY) << "Loading ConfigMaps data"; + try { + cereal::JSONInputArchive in_ar(in); + in_ar( + cereal::make_nvp("data", data) + ); + } catch (cereal::Exception &e) { + dbgError(D_LOCAL_POLICY) << "Failed to load ConfigMaps JSON. Error: " << e.what(); + return false; + } + return true; +} + +string +ConfigMaps::getFileContent() const +{ + if (data.size()) { + return data.begin()->second; + } + return string(); +} + +string +ConfigMaps::getFileName() const +{ + if (data.size()) { + return data.begin()->first; + } + return string(); +} +// LCOV_EXCL_STOP diff --git a/components/security_apps/orchestration/local_policy_mgmt_gen/exceptions_section.cc b/components/security_apps/local_policy_mgmt_gen/exceptions_section.cc old mode 100644 new mode 100755 similarity index 60% rename from components/security_apps/orchestration/local_policy_mgmt_gen/exceptions_section.cc rename to components/security_apps/local_policy_mgmt_gen/exceptions_section.cc index c9af836..8ad559c --- a/components/security_apps/orchestration/local_policy_mgmt_gen/exceptions_section.cc +++ b/components/security_apps/local_policy_mgmt_gen/exceptions_section.cc @@ -18,39 +18,61 @@ using namespace std; USE_DEBUG_FLAG(D_LOCAL_POLICY); // LCOV_EXCL_START Reason: no test exist +using AttributeGetter = function(const AppsecExceptionSpec&)>; +static const vector> attributes = { + {"countryCode", [](const AppsecExceptionSpec& e){ return e.getCountryCode(); }}, + {"countryName", [](const AppsecExceptionSpec& e){ return e.getCountryName(); }}, + {"hostName", [](const AppsecExceptionSpec& e){ return e.getHostName(); }}, + {"paramName", [](const AppsecExceptionSpec& e){ return e.getParamName(); }}, + {"paramValue", [](const AppsecExceptionSpec& e){ return e.getParamValue(); }}, + {"protectionName", [](const AppsecExceptionSpec& e){ return e.getProtectionName(); }}, + {"sourceIdentifier", [](const AppsecExceptionSpec& e){ return e.getSourceIdentifier(); }}, + {"sourceIp", [](const AppsecExceptionSpec& e){ return e.getSourceIp(); }}, + {"url", [](const AppsecExceptionSpec& e){ return e.getUrl(); }} +}; static const set valid_actions = {"skip", "accept", "drop", "suppressLog"}; +static const unordered_map key_to_action = { + { "accept", "accept"}, + { "drop", "reject"}, + { "skip", "ignore"}, + { "suppressLog", "ignore"} +}; void AppsecExceptionSpec::load(cereal::JSONInputArchive &archive_in) { dbgTrace(D_LOCAL_POLICY) << "Loading AppSec exception spec"; - parseAppsecJSONKey("name", name, archive_in); - parseAppsecJSONKey("action", action, archive_in); + parseAppsecJSONKey("action", action, archive_in, "skip"); if (valid_actions.count(action) == 0) { dbgWarning(D_LOCAL_POLICY) << "AppSec exception action invalid: " << action; } parseAppsecJSONKey>("countryCode", country_code, archive_in); + if (!country_code.empty()) conditions_number++; + parseAppsecJSONKey>("countryName", country_name, archive_in); + if (!country_name.empty()) conditions_number++; + parseAppsecJSONKey>("hostName", host_name, archive_in); + if (!host_name.empty()) conditions_number++; + parseAppsecJSONKey>("paramName", param_name, archive_in); + if (!param_name.empty()) conditions_number++; + parseAppsecJSONKey>("paramValue", param_value, archive_in); + if (!param_value.empty()) conditions_number++; + parseAppsecJSONKey>("protectionName", protection_name, archive_in); + if (!protection_name.empty()) conditions_number++; + parseAppsecJSONKey>("sourceIdentifier", source_identifier, archive_in); + if (!source_identifier.empty()) conditions_number++; + parseAppsecJSONKey>("sourceIp", source_ip, archive_in); + if (!source_ip.empty()) conditions_number++; + parseAppsecJSONKey>("url", url, archive_in); -} - -void -AppsecExceptionSpec::setName(const string &_name) -{ - name = _name; -} - -const string & -AppsecExceptionSpec::getName() const -{ - return name; + if (!url.empty()) conditions_number++; } const string & @@ -113,37 +135,82 @@ AppsecExceptionSpec::getUrl() const return url; } +bool +AppsecExceptionSpec::isOneCondition() const +{ + return conditions_number == 1; +} + +void +AppsecException::load(cereal::JSONInputArchive &archive_in) +{ + dbgTrace(D_LOCAL_POLICY) << "Loading AppSec exception"; + parseAppsecJSONKey("name", name, archive_in); + archive_in(CEREAL_NVP(exception_spec)); +} + +void +AppsecException::setName(const string &_name) +{ + name = _name; +} + +const string & +AppsecException::getName() const +{ + return name; +} + +const vector & +AppsecException::getExceptions() const +{ + return exception_spec; +} + ExceptionMatch::ExceptionMatch(const AppsecExceptionSpec &parsed_exception) : match_type(MatchType::Operator), op("and") { - if (!parsed_exception.getCountryCode().empty()) { - items.push_back(ExceptionMatch("countryCode", parsed_exception.getCountryCode())); + bool single_condition = parsed_exception.isOneCondition(); + for (auto &attrib : attributes) { + auto &attrib_name = attrib.first; + auto &attrib_getter = attrib.second; + auto exceptions_value = attrib_getter(parsed_exception); + if (exceptions_value.empty()) continue; + if (single_condition) { + if (exceptions_value.size() == 1) { + match_type = MatchType::Condition; + op = "equals"; + key = attrib_name; + value = exceptions_value; + return; + } else { + match_type = MatchType::Operator; + op = "or"; + for (auto new_value : exceptions_value) { + items.push_back(ExceptionMatch(attrib_name, {new_value})); + } + return; + } + } + items.push_back(ExceptionMatch(attrib_name, exceptions_value)); } - if (!parsed_exception.getCountryName().empty()) { - items.push_back(ExceptionMatch("countryName", parsed_exception.getCountryName())); - } - if (!parsed_exception.getHostName().empty()) { - items.push_back(ExceptionMatch("hostName", parsed_exception.getHostName())); - } - if (!parsed_exception.getParamName().empty()) { - items.push_back(ExceptionMatch("paramName", parsed_exception.getParamName())); - } - if (!parsed_exception.getParamValue().empty()) { - items.push_back(ExceptionMatch("paramValue", parsed_exception.getParamValue())); - } - if (!parsed_exception.getProtectionName().empty()) { - items.push_back(ExceptionMatch("protectionName", parsed_exception.getProtectionName())); - } - if (!parsed_exception.getSourceIdentifier().empty()) { - items.push_back(ExceptionMatch("sourceIdentifier", parsed_exception.getSourceIdentifier())); - } - if (!parsed_exception.getSourceIp().empty()) { - items.push_back(ExceptionMatch("sourceIp", parsed_exception.getSourceIp())); - } - if (!parsed_exception.getUrl().empty()) { - items.push_back(ExceptionMatch("url", parsed_exception.getUrl())); +} + +ExceptionMatch::ExceptionMatch(const std::string &_key, const std::vector &values) +{ + if (values.size() == 1) { + match_type = MatchType::Condition; + op = "equals"; + key = _key; + value = values; + } else { + match_type = MatchType::Operator; + op = "or"; + for (auto new_value : values) { + items.push_back(ExceptionMatch(_key, {new_value})); + } } } @@ -210,13 +277,34 @@ ExceptionMatch::save(cereal::JSONOutputArchive &out_ar) const } } -ExceptionBehavior::ExceptionBehavior( - const string &_key, - const string &_value) - : - key(_key), - value(_value) +const string & +ExceptionMatch::getOperator() const { + return op; +} + +const string & +ExceptionMatch::getKey() const +{ + return key; +} + +const string & +ExceptionMatch::getValue() const +{ + return value[0]; +} + +const vector & +ExceptionMatch::getMatch() const +{ + return items; +} + +ExceptionBehavior::ExceptionBehavior(const string &_value) +{ + key = _value == "suppressLog" ? "log" : "action"; + value = key_to_action.at(_value); try { id = to_string(boost::uuids::random_generator()()); } catch (const boost::uuids::entropy_error &e) { @@ -234,12 +322,31 @@ ExceptionBehavior::save(cereal::JSONOutputArchive &out_ar) const ); } -const string +const string & ExceptionBehavior::getBehaviorId() const { return id; } +const string & +ExceptionBehavior::getBehaviorKey() const +{ + return key; +} + +const string & +ExceptionBehavior::getBehaviorValue() const +{ + return value; +} + +InnerException::InnerException(ExceptionBehavior _behavior, ExceptionMatch _match) + : + behavior(_behavior), + match(_match) +{ +} + void InnerException::save(cereal::JSONOutputArchive &out_ar) const { @@ -249,12 +356,30 @@ InnerException::save(cereal::JSONOutputArchive &out_ar) const ); } -const string +const string & InnerException::getBehaviorId() const { return behavior.getBehaviorId(); } +const string & +InnerException::getBehaviorKey() const +{ + return behavior.getBehaviorKey(); +} + +const string & +InnerException::getBehaviorValue() const +{ + return behavior.getBehaviorValue(); +} + +const ExceptionMatch & +InnerException::getMatch() const +{ + return match; +} + ExceptionsRulebase::ExceptionsRulebase( vector _exceptions) : diff --git a/components/security_apps/orchestration/local_policy_mgmt_gen/include/access_control_practice.h b/components/security_apps/local_policy_mgmt_gen/include/access_control_practice.h old mode 100644 new mode 100755 similarity index 100% rename from components/security_apps/orchestration/local_policy_mgmt_gen/include/access_control_practice.h rename to components/security_apps/local_policy_mgmt_gen/include/access_control_practice.h diff --git a/components/security_apps/orchestration/local_policy_mgmt_gen/include/appsec_practice_section.h b/components/security_apps/local_policy_mgmt_gen/include/appsec_practice_section.h similarity index 94% rename from components/security_apps/orchestration/local_policy_mgmt_gen/include/appsec_practice_section.h rename to components/security_apps/local_policy_mgmt_gen/include/appsec_practice_section.h index 312f7de..a113c6b 100644 --- a/components/security_apps/orchestration/local_policy_mgmt_gen/include/appsec_practice_section.h +++ b/components/security_apps/local_policy_mgmt_gen/include/appsec_practice_section.h @@ -202,16 +202,35 @@ private: LogTriggerSection log; }; -class AppSecOverride +class ParsedMatch { public: - AppSecOverride(const SourcesIdentifiers &parsed_trusted_sources); + ParsedMatch() {} + ParsedMatch(const std::string &_operator, const std::string &_tag, const std::string &_value); + + ParsedMatch(const ExceptionMatch &exceptions); void save(cereal::JSONOutputArchive &out_ar) const; private: + std::string operator_type; + std::string tag; + std::string value; + std::vector parsed_match; +}; + +class AppSecOverride +{ +public: + AppSecOverride(const SourcesIdentifiers &parsed_trusted_sources); + AppSecOverride(const InnerException &parsed_exceptions); + + void save(cereal::JSONOutputArchive &out_ar) const; + +private: + std::string id; std::vector> parsed_behavior; - std::map parsed_match; + ParsedMatch parsed_match; }; class AppsecPracticeAntiBotSection @@ -254,7 +273,8 @@ public: const AppSecPracticeSpec &parsed_appsec_spec, const LogTriggerSection &parsed_log_trigger, const std::string &default_mode, - const AppSecTrustedSources &parsed_trusted_sources + const AppSecTrustedSources &parsed_trusted_sources, + const std::vector &parsed_exceptions ); WebAppSection( @@ -430,7 +450,7 @@ public: const std::vector &_practices, const std::vector &_log_triggers, const std::vector &_custom_responses, - const std::vector &_exceptions, + const std::vector &_exceptions, const std::vector &_trusted_sources, const std::vector &_sources_identifiers) : @@ -448,7 +468,7 @@ public: const std::vector & getAppSecPracticeSpecs() const; const std::vector & getAppsecTriggerSpecs() const; const std::vector & getAppSecCustomResponseSpecs() const; - const std::vector & getAppsecExceptionSpecs() const; + const std::vector & getAppsecExceptions() const; const std::vector & getAppsecTrustedSourceSpecs() const; const std::vector & getAppsecSourceIdentifierSpecs() const; void addSpecificRule(const ParsedRule &_rule); @@ -458,7 +478,7 @@ private: std::vector practices; std::vector log_triggers; std::vector custom_responses; - std::vector exceptions; + std::vector exceptions; std::vector trusted_sources; std::vector sources_identifiers; }; diff --git a/components/security_apps/local_policy_mgmt_gen/include/configmaps.h b/components/security_apps/local_policy_mgmt_gen/include/configmaps.h new file mode 100755 index 0000000..c289fb0 --- /dev/null +++ b/components/security_apps/local_policy_mgmt_gen/include/configmaps.h @@ -0,0 +1,41 @@ +// 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 __CONFIGMAPS_H__ +#define __CONFIGMAPS_H__ + +#include +#include + +#include "config.h" +#include "debug.h" +#include "rest.h" +#include "cereal/archives/json.hpp" +#include +#include "customized_cereal_map.h" + +#include "local_policy_common.h" + +class ConfigMaps : public ClientRest +{ +public: + bool loadJson(const std::string &json); + + std::string getFileContent() const; + std::string getFileName() const; + +private: + std::map data; +}; + +#endif // __CONFIGMAPS_H__ diff --git a/components/security_apps/orchestration/local_policy_mgmt_gen/include/exceptions_section.h b/components/security_apps/local_policy_mgmt_gen/include/exceptions_section.h similarity index 75% rename from components/security_apps/orchestration/local_policy_mgmt_gen/include/exceptions_section.h rename to components/security_apps/local_policy_mgmt_gen/include/exceptions_section.h index b07b0bb..e03ee27 100644 --- a/components/security_apps/orchestration/local_policy_mgmt_gen/include/exceptions_section.h +++ b/components/security_apps/local_policy_mgmt_gen/include/exceptions_section.h @@ -31,7 +31,6 @@ class AppsecExceptionSpec public: void load(cereal::JSONInputArchive &archive_in); - const std::string & getName() const; const std::string & getAction() const; const std::vector & getCountryCode() const; const std::vector & getCountryName() const; @@ -42,10 +41,10 @@ public: const std::vector & getSourceIdentifier() const; const std::vector & getSourceIp() const; const std::vector & getUrl() const; - void setName(const std::string &_name); + bool isOneCondition() const; private: - std::string name; + int conditions_number; std::string action; std::vector country_code; std::vector country_name; @@ -58,21 +57,42 @@ private: std::vector url; }; +class AppsecException +{ +public: + AppsecException() {}; + + // LCOV_EXCL_START Reason: no test exist + AppsecException(const std::string &_name, const std::vector &_exception_spec) + : + name(_name), + exception_spec(_exception_spec) {}; + // LCOV_EXCL_STOP + + void load(cereal::JSONInputArchive &archive_in); + + const std::string & getName() const; + const std::vector & getExceptions() const; + void setName(const std::string &_name); + +private: + std::string name; + std::vector exception_spec; +}; + class ExceptionMatch { public: ExceptionMatch() {} ExceptionMatch(const AppsecExceptionSpec &parsed_exception); + ExceptionMatch(const std::string &_key, const std::vector &_value); ExceptionMatch(const NewAppsecException &parsed_exception); - ExceptionMatch(const std::string &_key, const std::vector &_value) - : - match_type(MatchType::Condition), - key(_key), - op("in"), - value(_value) - {} void save(cereal::JSONOutputArchive &out_ar) const; + const std::string & getOperator() const; + const std::string & getKey() const; + const std::string & getValue() const; + const std::vector & getMatch() const; private: MatchType match_type; @@ -86,13 +106,12 @@ class ExceptionBehavior { public: ExceptionBehavior() {} - ExceptionBehavior( - const std::string &_key, - const std::string &_value - ); + ExceptionBehavior(const std::string &_value); void save(cereal::JSONOutputArchive &out_ar) const; - const std::string getBehaviorId() const; + const std::string & getBehaviorId() const; + const std::string & getBehaviorKey() const; + const std::string & getBehaviorValue() const; private: std::string key; @@ -104,15 +123,13 @@ class InnerException { public: InnerException() {} - InnerException( - ExceptionBehavior _behavior, - ExceptionMatch _match) - : - behavior(_behavior), - match(_match) {} + InnerException(ExceptionBehavior _behavior, ExceptionMatch _match); void save(cereal::JSONOutputArchive &out_ar) const; - const std::string getBehaviorId() const; + const std::string & getBehaviorId() const; + const std::string & getBehaviorKey() const; + const std::string & getBehaviorValue() const; + const ExceptionMatch & getMatch() const; private: ExceptionBehavior behavior; diff --git a/components/security_apps/orchestration/local_policy_mgmt_gen/include/ingress_data.h b/components/security_apps/local_policy_mgmt_gen/include/ingress_data.h similarity index 100% rename from components/security_apps/orchestration/local_policy_mgmt_gen/include/ingress_data.h rename to components/security_apps/local_policy_mgmt_gen/include/ingress_data.h diff --git a/components/security_apps/orchestration/local_policy_mgmt_gen/include/k8s_policy_utils.h b/components/security_apps/local_policy_mgmt_gen/include/k8s_policy_utils.h similarity index 92% rename from components/security_apps/orchestration/local_policy_mgmt_gen/include/k8s_policy_utils.h rename to components/security_apps/local_policy_mgmt_gen/include/k8s_policy_utils.h index 7c5764d..99cefc2 100644 --- a/components/security_apps/orchestration/local_policy_mgmt_gen/include/k8s_policy_utils.h +++ b/components/security_apps/local_policy_mgmt_gen/include/k8s_policy_utils.h @@ -47,7 +47,7 @@ public: std::tuple, std::map> createAppsecPoliciesFromIngresses(); - bool getClusterId() const; + void getClusterId() const; private: std::map parseIngressAnnotations( @@ -67,12 +67,19 @@ private: const NewParsedRule &default_rule ) const; + std::vector extractExceptionsFromCluster( + const std::string &crd_plural, + const std::unordered_set &elements_names + ) const; + template std::vector extractElementsFromCluster( const std::string &crd_plural, const std::unordered_set &elements_names ) const; + void createSnortFile(std::vector &practices) const; + template std::vector extractV1Beta2ElementsFromCluster( const std::string &crd_plural, diff --git a/components/security_apps/orchestration/local_policy_mgmt_gen/include/local_policy_common.h b/components/security_apps/local_policy_mgmt_gen/include/local_policy_common.h similarity index 100% rename from components/security_apps/orchestration/local_policy_mgmt_gen/include/local_policy_common.h rename to components/security_apps/local_policy_mgmt_gen/include/local_policy_common.h diff --git a/components/security_apps/orchestration/local_policy_mgmt_gen/include/new_appsec_linux_policy.h b/components/security_apps/local_policy_mgmt_gen/include/new_appsec_linux_policy.h old mode 100644 new mode 100755 similarity index 97% rename from components/security_apps/orchestration/local_policy_mgmt_gen/include/new_appsec_linux_policy.h rename to components/security_apps/local_policy_mgmt_gen/include/new_appsec_linux_policy.h index d52c224..5b93901 --- a/components/security_apps/orchestration/local_policy_mgmt_gen/include/new_appsec_linux_policy.h +++ b/components/security_apps/local_policy_mgmt_gen/include/new_appsec_linux_policy.h @@ -65,7 +65,7 @@ public: const std::vector & getAccessControlPracticeSpecs() const; const std::vector & getAppsecTriggerSpecs() const; const std::vector & getAppSecCustomResponseSpecs() const; - const std::vector & getAppsecExceptionSpecs() const; + const std::vector & getAppsecExceptions() const; const std::vector & getAppsecTrustedSourceSpecs() const; const std::vector & getAppsecSourceIdentifierSpecs() const; void addSpecificRule(const NewParsedRule &_rule); diff --git a/components/security_apps/orchestration/local_policy_mgmt_gen/include/new_appsec_policy_crd_parser.h b/components/security_apps/local_policy_mgmt_gen/include/new_appsec_policy_crd_parser.h old mode 100644 new mode 100755 similarity index 100% rename from components/security_apps/orchestration/local_policy_mgmt_gen/include/new_appsec_policy_crd_parser.h rename to components/security_apps/local_policy_mgmt_gen/include/new_appsec_policy_crd_parser.h diff --git a/components/security_apps/orchestration/local_policy_mgmt_gen/include/new_custom_response.h b/components/security_apps/local_policy_mgmt_gen/include/new_custom_response.h old mode 100644 new mode 100755 similarity index 100% rename from components/security_apps/orchestration/local_policy_mgmt_gen/include/new_custom_response.h rename to components/security_apps/local_policy_mgmt_gen/include/new_custom_response.h diff --git a/components/security_apps/orchestration/local_policy_mgmt_gen/include/new_exceptions.h b/components/security_apps/local_policy_mgmt_gen/include/new_exceptions.h old mode 100644 new mode 100755 similarity index 100% rename from components/security_apps/orchestration/local_policy_mgmt_gen/include/new_exceptions.h rename to components/security_apps/local_policy_mgmt_gen/include/new_exceptions.h diff --git a/components/security_apps/orchestration/local_policy_mgmt_gen/include/new_log_trigger.h b/components/security_apps/local_policy_mgmt_gen/include/new_log_trigger.h old mode 100644 new mode 100755 similarity index 100% rename from components/security_apps/orchestration/local_policy_mgmt_gen/include/new_log_trigger.h rename to components/security_apps/local_policy_mgmt_gen/include/new_log_trigger.h diff --git a/components/security_apps/orchestration/local_policy_mgmt_gen/include/new_practice.h b/components/security_apps/local_policy_mgmt_gen/include/new_practice.h old mode 100644 new mode 100755 similarity index 62% rename from components/security_apps/orchestration/local_policy_mgmt_gen/include/new_practice.h rename to components/security_apps/local_policy_mgmt_gen/include/new_practice.h index 553fb06..c9f6375 --- a/components/security_apps/orchestration/local_policy_mgmt_gen/include/new_practice.h +++ b/components/security_apps/local_policy_mgmt_gen/include/new_practice.h @@ -147,25 +147,25 @@ public: // LCOV_EXCL_STOP FileSecurityProtectionsSection( - int file_size_limit, - int archive_file_size_limit, - bool allow_files_without_name, - bool required_file_size_limit, - bool required_archive_extraction, - const std::string &context, - const std::string &name, - const std::string &asset_id, - const std::string &practice_name, - const std::string &practice_id, - const std::string &action, - const std::string &files_without_name_action, - const std::string &high_confidence_action, - const std::string &medium_confidence_action, - const std::string &low_confidence_action, - const std::string &severity_level, - const std::string &fileSize_limit_action, - const std::string &multi_level_archive_action, - const std::string &unopened_archive_actio + int _file_size_limit, + int _archive_file_size_limit, + bool _allow_files_without_name, + bool _required_file_size_limit, + bool _required_archive_extraction, + const std::string &_context, + const std::string &_name, + const std::string &_asset_id, + const std::string &_practice_name, + const std::string &_practice_id, + const std::string &_action, + const std::string &_files_without_name_action, + const std::string &_high_confidence_action, + const std::string &_medium_confidence_action, + const std::string &_low_confidence_action, + const std::string &_severity_level, + const std::string &_file_size_limit_action, + const std::string &_multi_level_archive_action, + const std::string &_unopened_archive_action ); void save(cereal::JSONOutputArchive &out_ar) const; @@ -265,6 +265,7 @@ class NewFileSecurity public: void load(cereal::JSONInputArchive &archive_in); + const std::string & getOverrideMode() const; const NewFileSecurityArchiveInspection & getArchiveInspection() const; const NewFileSecurityLargeFileInspection & getLargeFileInspection() const; FileSecurityProtectionsSection createFileSecurityProtectionsSection( @@ -287,17 +288,210 @@ private: NewFileSecurityLargeFileInspection large_file_inspection; }; +class SnortProtectionsSection +{ +public: + // LCOV_EXCL_START Reason: no test exist + SnortProtectionsSection() {}; + // LCOV_EXCL_STOP + + SnortProtectionsSection( + const std::string &_context, + const std::string &_asset_name, + const std::string &_asset_id, + const std::string &_practice_name, + const std::string &_practice_id, + const std::string &_source_identifier, + const std::string &_mode, + const std::vector &_files + ); + + void save(cereal::JSONOutputArchive &out_ar) const; + +private: + std::string context; + std::string asset_name; + std::string asset_id; + std::string practice_name; + std::string practice_id; + std::string source_identifier; + std::string mode; + std::vector files; +}; + +class DetectionRules +{ +public: + // LCOV_EXCL_START Reason: no test exist + DetectionRules() {}; + // LCOV_EXCL_STOP + + DetectionRules( + const std::string &_type, + const std::string &_SSM, + const std::string &_keywords, + const std::vector &_context + ); + + void load(cereal::JSONInputArchive &archive_in); + void save(cereal::JSONOutputArchive &out_ar) const; + +private: + std::string type; + std::string SSM; + std::string keywords; + std::vector context; +}; + +class ProtectionMetadata +{ +public: + // LCOV_EXCL_START Reason: no test exist + ProtectionMetadata() {}; + // LCOV_EXCL_STOP + + ProtectionMetadata( + bool _silent, + const std::string &_protection_name, + const std::string &_severity, + const std::string &_confidence_level, + const std::string &_performance_impact, + const std::string &_last_update, + const std::string &_maintrain_id, + const std::vector &_tags, + const std::vector &_cve_list + ); + + void load(cereal::JSONInputArchive &archive_in); + void save(cereal::JSONOutputArchive &out_ar) const; + +private: + bool silent; + std::string protection_name; + std::string severity; + std::string confidence_level; + std::string performance_impact; + std::string last_update; + std::string maintrain_id; + std::vector tags; + std::vector cve_list; +}; + +class ProtectionsProtectionsSection +{ +public: + // LCOV_EXCL_START Reason: no test exist + ProtectionsProtectionsSection() {}; + // LCOV_EXCL_STOP + + ProtectionsProtectionsSection( + const ProtectionMetadata &_protection_metadata, + const DetectionRules &_detection_rules + ); + + void load(cereal::JSONInputArchive &archive_in); + void save(cereal::JSONOutputArchive &out_ar) const; + +private: + ProtectionMetadata protection_metadata; + DetectionRules detection_rules; +}; + +class ProtectionsSection +{ +public: + // LCOV_EXCL_START Reason: no test exist + ProtectionsSection() {}; + // LCOV_EXCL_STOP + + ProtectionsSection( + const std::vector &_protections, + const std::string &_name = "", + const std::string &_modification_time = "" + ); + + void load(cereal::JSONInputArchive &archive_in); + void save(cereal::JSONOutputArchive &out_ar) const; + const std::vector & getProtections() const; + +private: + std::vector protections; + std::string name; + std::string modification_time; +}; + +class ProtectionsSectionWrapper +{ +public: + // LCOV_EXCL_START Reason: no test exist + ProtectionsSectionWrapper() {}; + // LCOV_EXCL_STOP + + void serialize(cereal::JSONInputArchive &archive_in); + const std::vector & getProtections() const; + +private: + ProtectionsSection protections; +}; + +class SnortSection +{ +public: + // LCOV_EXCL_START Reason: no test exist + SnortSection() {}; + + SnortSection( + const std::vector &_snort, + const std::vector &_protections) + : + snort_protections(_snort), + protections(_protections) + {}; + // LCOV_EXCL_STOP + + void load(cereal::JSONInputArchive &archive_in); + void save(cereal::JSONOutputArchive &out_ar) const; + const std::vector & getProtections() const; + +private: + std::vector snort_protections; + std::vector protections; +}; + +class SnortSectionWrapper +{ +public: + // LCOV_EXCL_START Reason: no test exist + SnortSectionWrapper() {}; + + SnortSectionWrapper( + const std::vector &_snort, + const std::vector &_protections) + : + snort(SnortSection(_snort, _protections)) + {}; + // LCOV_EXCL_STOP + + void save(cereal::JSONOutputArchive &out_ar) const; + +private: + SnortSection snort; +}; + class NewSnortSignaturesAndOpenSchemaAPI { public: void load(cereal::JSONInputArchive &archive_in); + void addFile(const std::string &file_name); const std::string & getOverrideMode() const; const std::vector & getConfigMap() const; + const std::vector & getFiles() const; private: std::string override_mode; std::vector config_map; + std::vector files; }; class NewAppSecWebBotsURI @@ -371,8 +565,8 @@ class NewAppSecPracticeSpec public: void load(cereal::JSONInputArchive &archive_in); + NewSnortSignaturesAndOpenSchemaAPI & getSnortSignatures(); const NewSnortSignaturesAndOpenSchemaAPI & getOpenSchemaValidation() const; - const NewSnortSignaturesAndOpenSchemaAPI & getSnortSignatures() const; const NewAppSecPracticeWebAttacks & getWebAttacks() const; const NewAppSecPracticeAntiBot & getAntiBot() const; const NewIntrusionPrevention & getIntrusionPrevention() const; diff --git a/components/security_apps/orchestration/local_policy_mgmt_gen/include/new_trusted_sources.h b/components/security_apps/local_policy_mgmt_gen/include/new_trusted_sources.h old mode 100644 new mode 100755 similarity index 100% rename from components/security_apps/orchestration/local_policy_mgmt_gen/include/new_trusted_sources.h rename to components/security_apps/local_policy_mgmt_gen/include/new_trusted_sources.h diff --git a/components/security_apps/orchestration/local_policy_mgmt_gen/include/policy_maker_utils.h b/components/security_apps/local_policy_mgmt_gen/include/policy_maker_utils.h old mode 100644 new mode 100755 similarity index 79% rename from components/security_apps/orchestration/local_policy_mgmt_gen/include/policy_maker_utils.h rename to components/security_apps/local_policy_mgmt_gen/include/policy_maker_utils.h index 1e8b9b1..2436766 --- a/components/security_apps/orchestration/local_policy_mgmt_gen/include/policy_maker_utils.h +++ b/components/security_apps/local_policy_mgmt_gen/include/policy_maker_utils.h @@ -57,19 +57,21 @@ class SecurityAppsWrapper { public: SecurityAppsWrapper( - const AppSecWrapper &_waap, - const TriggersWrapper &_trrigers, - const RulesConfigWrapper &_rules, - const IntrusionPreventionWrapper &_ips, - const AccessControlRulebaseWrapper &_rate_limit, - const FileSecurityWrapper &_file_security, - const ExceptionsWrapper &_exceptions, - const std::string &_policy_version) + const AppSecWrapper &_waap, + const TriggersWrapper &_trrigers, + const RulesConfigWrapper &_rules, + const IntrusionPreventionWrapper &_ips, + const SnortSectionWrapper &_snort, + const AccessControlRulebaseWrapper &_rate_limit, + const FileSecurityWrapper &_file_security, + const ExceptionsWrapper &_exceptions, + const std::string &_policy_version) : waap(_waap), trrigers(_trrigers), rules(_rules), ips(_ips), + snort(_snort), rate_limit(_rate_limit), file_security(_file_security), exceptions(_exceptions), @@ -78,14 +80,15 @@ public: void save(cereal::JSONOutputArchive &out_ar) const; private: - AppSecWrapper waap; - TriggersWrapper trrigers; - RulesConfigWrapper rules; - IntrusionPreventionWrapper ips; - AccessControlRulebaseWrapper rate_limit; - FileSecurityWrapper file_security; - ExceptionsWrapper exceptions; - std::string policy_version; + AppSecWrapper waap; + TriggersWrapper trrigers; + RulesConfigWrapper rules; + IntrusionPreventionWrapper ips; + SnortSectionWrapper snort; + AccessControlRulebaseWrapper rate_limit; + FileSecurityWrapper file_security; + ExceptionsWrapper exceptions; + std::string policy_version; }; class PolicyWrapper @@ -129,7 +132,8 @@ public: private: std::string getPolicyName(const std::string &policy_path); - Maybe openPolicyAsJson(const std::string &policy_path); + template + Maybe openFileAsJson(const std::string &path); void clearElementsMaps(); @@ -155,6 +159,20 @@ private: std::map &rule_annotations ); + void createSnortProtecionsSection(const std::string &file_name, const std::string &practic_name); + + void + createSnortSections( + const std::string & context, + const std::string &asset_name, + const std::string &asset_id, + const std::string &practice_name, + const std::string &practice_id, + const std::string &source_identifier, + const V1beta2AppsecLinuxPolicy &policy, + std::map &rule_annotations + ); + void createFileSecuritySections( const std::string &asset_id, @@ -215,10 +233,12 @@ private: std::map log_triggers; std::map web_user_res_triggers; - std::map inner_exceptions; + std::map> inner_exceptions; std::map web_apps; std::map rules_config; std::map ips; + std::map snort; + std::map snort_protections; std::map file_security; std::map rate_limit; std::map users_identifiers; diff --git a/components/security_apps/orchestration/local_policy_mgmt_gen/include/rules_config_section.h b/components/security_apps/local_policy_mgmt_gen/include/rules_config_section.h similarity index 100% rename from components/security_apps/orchestration/local_policy_mgmt_gen/include/rules_config_section.h rename to components/security_apps/local_policy_mgmt_gen/include/rules_config_section.h diff --git a/components/security_apps/orchestration/local_policy_mgmt_gen/include/settings_section.h b/components/security_apps/local_policy_mgmt_gen/include/settings_section.h similarity index 100% rename from components/security_apps/orchestration/local_policy_mgmt_gen/include/settings_section.h rename to components/security_apps/local_policy_mgmt_gen/include/settings_section.h diff --git a/components/security_apps/orchestration/local_policy_mgmt_gen/include/snort_section.h b/components/security_apps/local_policy_mgmt_gen/include/snort_section.h similarity index 100% rename from components/security_apps/orchestration/local_policy_mgmt_gen/include/snort_section.h rename to components/security_apps/local_policy_mgmt_gen/include/snort_section.h diff --git a/components/security_apps/orchestration/local_policy_mgmt_gen/include/triggers_section.h b/components/security_apps/local_policy_mgmt_gen/include/triggers_section.h similarity index 100% rename from components/security_apps/orchestration/local_policy_mgmt_gen/include/triggers_section.h rename to components/security_apps/local_policy_mgmt_gen/include/triggers_section.h diff --git a/components/security_apps/orchestration/local_policy_mgmt_gen/include/trusted_sources_section.h b/components/security_apps/local_policy_mgmt_gen/include/trusted_sources_section.h similarity index 100% rename from components/security_apps/orchestration/local_policy_mgmt_gen/include/trusted_sources_section.h rename to components/security_apps/local_policy_mgmt_gen/include/trusted_sources_section.h diff --git a/components/security_apps/orchestration/local_policy_mgmt_gen/ingress_data.cc b/components/security_apps/local_policy_mgmt_gen/ingress_data.cc old mode 100644 new mode 100755 similarity index 100% rename from components/security_apps/orchestration/local_policy_mgmt_gen/ingress_data.cc rename to components/security_apps/local_policy_mgmt_gen/ingress_data.cc diff --git a/components/security_apps/orchestration/local_policy_mgmt_gen/k8s_policy_utils.cc b/components/security_apps/local_policy_mgmt_gen/k8s_policy_utils.cc similarity index 88% rename from components/security_apps/orchestration/local_policy_mgmt_gen/k8s_policy_utils.cc rename to components/security_apps/local_policy_mgmt_gen/k8s_policy_utils.cc index fe88182..976ae85 100644 --- a/components/security_apps/orchestration/local_policy_mgmt_gen/k8s_policy_utils.cc +++ b/components/security_apps/local_policy_mgmt_gen/k8s_policy_utils.cc @@ -12,7 +12,7 @@ // limitations under the License. #include "k8s_policy_utils.h" -#include "namespace_data.h" +#include "configmaps.h" using namespace std; @@ -184,6 +184,36 @@ getAppSecClassNameFromCluster() } // LCOV_EXCL_STOP +vector +K8sPolicyUtils::extractExceptionsFromCluster( + const string &crd_plural, + const unordered_set &elements_names) const +{ + dbgTrace(D_LOCAL_POLICY) << "Retrieve AppSec elements. type: " << crd_plural; + vector elements; + for (const string &element_name : elements_names) { + dbgTrace(D_LOCAL_POLICY) << "AppSec element name: " << element_name; + auto maybe_appsec_element = getObjectFromCluster>>( + "/apis/openappsec.io/v1beta1/" + crd_plural + "/" + element_name + ); + + if (!maybe_appsec_element.ok()) { + dbgWarning(D_LOCAL_POLICY) + << "Failed to retrieve AppSec element. type: " + << crd_plural + << ", name: " + << element_name + << ". Error: " + << maybe_appsec_element.getErr(); + continue; + } + + AppsecSpecParser> appsec_element = maybe_appsec_element.unpack(); + elements.push_back(AppsecException(element_name, appsec_element.getSpec())); + } + return elements; +} + template vector K8sPolicyUtils::extractElementsFromCluster( @@ -292,7 +322,8 @@ K8sPolicyUtils::createAppsecPolicyK8sFromV1beta1Crds( policy_elements_names[AnnotationTypes::WEB_USER_RES] ); - vector exceptions = extractElementsFromCluster( + + vector exceptions = extractExceptionsFromCluster( "exceptions", policy_elements_names[AnnotationTypes::EXCEPTION] ); @@ -320,6 +351,34 @@ K8sPolicyUtils::createAppsecPolicyK8sFromV1beta1Crds( } // LCOV_EXCL_START Reason: no test exist +void +K8sPolicyUtils::createSnortFile(vector &practices) const +{ + for (NewAppSecPracticeSpec &practice : practices) { + auto orchestration_tools = Singleton::Consume::by(); + auto path = "/etc/cp/conf/snort/snort_k8s_" + practice.getName() + ".rule"; + bool append_mode = false; + for (const string &config_map : practice.getSnortSignatures().getConfigMap()) + { + auto maybe_configmap = getObjectFromCluster( + "/api/v1/namespaces/default/configmaps/" + config_map + ); + if (!maybe_configmap.ok()) { + dbgWarning(D_LOCAL_POLICY) << "Failed to get configMaps from the cluster."; + continue; + } + string file_content = maybe_configmap.unpack().getFileContent(); + string file_name = maybe_configmap.unpack().getFileName(); + if (!orchestration_tools->writeFile(file_content, path, append_mode)) { + dbgWarning(D_LOCAL_POLICY) << "Failed to update the snort_k8s_rules file."; + continue; + } + append_mode = true; + practice.getSnortSignatures().addFile(file_name); + } + } +} + Maybe K8sPolicyUtils::createAppsecPolicyK8sFromV1beta2Crds( const AppsecSpecParser &appsec_policy_spec, @@ -349,6 +408,8 @@ K8sPolicyUtils::createAppsecPolicyK8sFromV1beta2Crds( policy_elements_names[AnnotationTypes::THREAT_PREVENTION_PRACTICE] ); + createSnortFile(threat_prevention_practices); + vector access_control_practices = extractV1Beta2ElementsFromCluster( "accesscontrolpractice", @@ -404,7 +465,7 @@ doesVersionExist(const map &annotations, const string &version) } return false; } -//need to refactor don't forget that + std::tuple, Maybe> K8sPolicyUtils::createAppsecPolicyK8s(const string &policy_name, const string &ingress_mode) const { @@ -524,50 +585,3 @@ K8sPolicyUtils::createAppsecPoliciesFromIngresses() } return make_tuple(v1bet1_policies, v1bet2_policies); } - -bool -isPlaygroundEnv() -{ - const char *env_string = getenv("PLAYGROUND"); - - if (env_string == nullptr) return false; - string env_value = env_string; - transform(env_value.begin(), env_value.end(), env_value.begin(), ::tolower); - - return env_value == "true"; -} - -bool -K8sPolicyUtils::getClusterId() const -{ - string playground_uid = isPlaygroundEnv() ? "playground-" : ""; - - dbgTrace(D_LOCAL_POLICY) << "Getting cluster UID"; - auto maybe_namespaces_data = getObjectFromCluster("/api/v1/namespaces/"); - - if (!maybe_namespaces_data.ok()) { - dbgWarning(D_LOCAL_POLICY) - << "Failed to retrieve K8S namespace data. Error: " - << maybe_namespaces_data.getErr(); - return false; - } - - NamespaceData namespaces_data = maybe_namespaces_data.unpack(); - - Maybe maybe_ns_uid = namespaces_data.getNamespaceUidByName("kube-system"); - if (!maybe_ns_uid.ok()) { - dbgWarning(D_LOCAL_POLICY) << maybe_ns_uid.getErr(); - return false; - } - string uid = playground_uid + maybe_ns_uid.unpack(); - dbgTrace(D_LOCAL_POLICY) << "Found k8s cluster UID: " << uid; - I_Environment *env = Singleton::Consume::by(); - env->getConfigurationContext().registerValue( - "k8sClusterId", - uid, - EnvKeyAttr::LogSection::SOURCE - ); - I_AgentDetails *i_agent_details = Singleton::Consume::by(); - i_agent_details->setClusterId(uid); - return true; -} diff --git a/components/security_apps/orchestration/local_policy_mgmt_gen/local_policy_mgmt_gen.cc b/components/security_apps/local_policy_mgmt_gen/local_policy_mgmt_gen.cc similarity index 68% rename from components/security_apps/orchestration/local_policy_mgmt_gen/local_policy_mgmt_gen.cc rename to components/security_apps/local_policy_mgmt_gen/local_policy_mgmt_gen.cc index 1016d94..f246783 100644 --- a/components/security_apps/orchestration/local_policy_mgmt_gen/local_policy_mgmt_gen.cc +++ b/components/security_apps/local_policy_mgmt_gen/local_policy_mgmt_gen.cc @@ -43,7 +43,7 @@ #include "include/rules_config_section.h" #include "include/trusted_sources_section.h" #include "include/policy_maker_utils.h" -#include "include/k8s_policy_utils.h" +#include "k8s_policy_utils.h" #include "i_env_details.h" using namespace std; @@ -64,31 +64,10 @@ public: void init() { - env_details = Singleton::Consume::by(); - env_type = env_details->getEnvType(); - if (env_type == EnvType::LINUX) { - dbgInfo(D_LOCAL_POLICY) << "Initializing Linux policy generator"; - local_policy_path = getFilesystemPathConfig() + default_local_mgmt_policy_path; - return; - } - dbgInfo(D_LOCAL_POLICY) << "Initializing K8S policy generator"; - k8s_policy_utils.init(); - - Singleton::Consume::by()->addOneTimeRoutine( - I_MainLoop::RoutineType::Offline, - [this] () - { - while(!k8s_policy_utils.getClusterId()) { - Singleton::Consume::by()->yield(chrono::seconds(1)); - } - return; - }, - "Get k8s cluster ID" - ); } string - parseLinuxPolicy(const string &policy_version) + parseLinuxPolicy(const string &policy_version, const string &local_policy_path) { dbgFlow(D_LOCAL_POLICY) << "Starting to parse policy - embedded environment"; @@ -104,6 +83,10 @@ public: { dbgFlow(D_LOCAL_POLICY) << "Starting to parse policy - K8S environment"; + dbgInfo(D_LOCAL_POLICY) << "Initializing K8S policy generator"; + K8sPolicyUtils k8s_policy_utils; + k8s_policy_utils.init(); + auto appsec_policies = k8s_policy_utils.createAppsecPoliciesFromIngresses(); if (!std::get<0>(appsec_policies).empty()) { return policy_maker_utils.proccesMultipleAppsecPolicies( @@ -120,27 +103,14 @@ public: } string - parsePolicy(const string &policy_version) + generateAppSecLocalPolicy(EnvType env_type, const string &policy_version, const string &local_policy_paths) { - return isK8sEnv() ? parseK8sPolicy(policy_version) : parseLinuxPolicy(policy_version); + return env_type == EnvType::K8S ? + parseK8sPolicy(policy_version) : parseLinuxPolicy(policy_version, local_policy_paths); } - const string & getAgentPolicyPath(void) const override { return default_local_appsec_policy_path; } - const string & getLocalPolicyPath(void) const override { return local_policy_path; } - void setPolicyPath(const string &new_local_policy_path) override { local_policy_path = new_local_policy_path; } - private: - bool - isK8sEnv() - { - return env_type == EnvType::K8S; - } - - I_EnvDetails* env_details = nullptr; - EnvType env_type; PolicyMakerUtils policy_maker_utils; - K8sPolicyUtils k8s_policy_utils; - string local_policy_path; }; diff --git a/components/security_apps/orchestration/local_policy_mgmt_gen/new_appsec_linux_policy.cc b/components/security_apps/local_policy_mgmt_gen/new_appsec_linux_policy.cc old mode 100644 new mode 100755 similarity index 96% rename from components/security_apps/orchestration/local_policy_mgmt_gen/new_appsec_linux_policy.cc rename to components/security_apps/local_policy_mgmt_gen/new_appsec_linux_policy.cc index 34d6b16..24dc962 --- a/components/security_apps/orchestration/local_policy_mgmt_gen/new_appsec_linux_policy.cc +++ b/components/security_apps/local_policy_mgmt_gen/new_appsec_linux_policy.cc @@ -47,7 +47,7 @@ V1beta2AppsecLinuxPolicy::getAppSecCustomResponseSpecs() const } const vector & -V1beta2AppsecLinuxPolicy::getAppsecExceptionSpecs() const +V1beta2AppsecLinuxPolicy::getAppsecExceptions() const { return exceptions; } diff --git a/components/security_apps/orchestration/local_policy_mgmt_gen/new_appsec_policy_crd_parser.cc b/components/security_apps/local_policy_mgmt_gen/new_appsec_policy_crd_parser.cc old mode 100644 new mode 100755 similarity index 100% rename from components/security_apps/orchestration/local_policy_mgmt_gen/new_appsec_policy_crd_parser.cc rename to components/security_apps/local_policy_mgmt_gen/new_appsec_policy_crd_parser.cc diff --git a/components/security_apps/orchestration/local_policy_mgmt_gen/new_custom_response.cc b/components/security_apps/local_policy_mgmt_gen/new_custom_response.cc old mode 100644 new mode 100755 similarity index 100% rename from components/security_apps/orchestration/local_policy_mgmt_gen/new_custom_response.cc rename to components/security_apps/local_policy_mgmt_gen/new_custom_response.cc diff --git a/components/security_apps/orchestration/local_policy_mgmt_gen/new_exceptions.cc b/components/security_apps/local_policy_mgmt_gen/new_exceptions.cc old mode 100644 new mode 100755 similarity index 100% rename from components/security_apps/orchestration/local_policy_mgmt_gen/new_exceptions.cc rename to components/security_apps/local_policy_mgmt_gen/new_exceptions.cc diff --git a/components/security_apps/orchestration/local_policy_mgmt_gen/new_log_trigger.cc b/components/security_apps/local_policy_mgmt_gen/new_log_trigger.cc old mode 100644 new mode 100755 similarity index 100% rename from components/security_apps/orchestration/local_policy_mgmt_gen/new_log_trigger.cc rename to components/security_apps/local_policy_mgmt_gen/new_log_trigger.cc diff --git a/components/security_apps/orchestration/local_policy_mgmt_gen/new_practice.cc b/components/security_apps/local_policy_mgmt_gen/new_practice.cc old mode 100644 new mode 100755 similarity index 77% rename from components/security_apps/orchestration/local_policy_mgmt_gen/new_practice.cc rename to components/security_apps/local_policy_mgmt_gen/new_practice.cc index 9ae3141..ab2fd01 --- a/components/security_apps/orchestration/local_policy_mgmt_gen/new_practice.cc +++ b/components/security_apps/local_policy_mgmt_gen/new_practice.cc @@ -217,23 +217,253 @@ NewAppSecPracticeWebAttacks::getMode(const string &default_mode) const return key_to_practices_val.at(mode); } +SnortProtectionsSection::SnortProtectionsSection( + const std::string &_context, + const std::string &_asset_name, + const std::string &_asset_id, + const std::string &_practice_name, + const std::string &_practice_id, + const std::string &_source_identifier, + const std::string &_mode, + const std::vector &_files) + : + context(_context), + asset_name(_asset_name), + asset_id(_asset_id), + practice_name(_practice_name), + practice_id(_practice_id), + source_identifier(_source_identifier), + mode(_mode), + files(_files) +{ +} + +void +SnortProtectionsSection::save(cereal::JSONOutputArchive &out_ar) const +{ + out_ar( + cereal::make_nvp("context", context), + cereal::make_nvp("mode", key_to_mode_val.at(mode)), + cereal::make_nvp("files", files), + cereal::make_nvp("assetName", asset_name), + cereal::make_nvp("assetId", asset_id), + cereal::make_nvp("practiceName", practice_name), + cereal::make_nvp("practiceId", practice_id), + cereal::make_nvp("sourceIdentifier", source_identifier) + ); +} + +DetectionRules::DetectionRules( + const std::string &_type, + const std::string &_SSM, + const std::string &_keywords, + const std::vector &_context) + : + type(_type), + SSM(_SSM), + keywords(_keywords), + context(_context) +{ +} + +void +DetectionRules::load(cereal::JSONInputArchive &archive_in) +{ + dbgTrace(D_LOCAL_POLICY) << "Loading Snort protections protections detection rules section"; + parseAppsecJSONKey("type", type, archive_in); + parseAppsecJSONKey("SSM", SSM, archive_in); + parseAppsecJSONKey("keywords", keywords, archive_in); + parseAppsecJSONKey>("context", context, archive_in); + +} + +void +DetectionRules::save(cereal::JSONOutputArchive &out_ar) const +{ + out_ar( + cereal::make_nvp("type", type), + cereal::make_nvp("SSM", SSM), + cereal::make_nvp("keywords", keywords), + cereal::make_nvp("context", context) + ); +} + +ProtectionMetadata::ProtectionMetadata( + bool _silent, + const std::string &_protection_name, + const std::string &_severity, + const std::string &_confidence_level, + const std::string &_performance_impact, + const std::string &_last_update, + const std::string &_maintrain_id, + const std::vector &_tags, + const std::vector &_cve_list) + : + silent(_silent), + protection_name(_protection_name), + severity(_severity), + confidence_level(_confidence_level), + performance_impact(_performance_impact), + last_update(_last_update), + maintrain_id(_maintrain_id), + tags(_tags), + cve_list(_cve_list) +{ +} + +void +ProtectionMetadata::load(cereal::JSONInputArchive &archive_in) +{ + dbgTrace(D_LOCAL_POLICY) << "Loading Snort protections protections metadata section"; + parseAppsecJSONKey("silent", silent, archive_in); + parseAppsecJSONKey("protectionName", protection_name, archive_in); + parseAppsecJSONKey("severity", severity, archive_in); + parseAppsecJSONKey("confidenceLevel", confidence_level, archive_in); + parseAppsecJSONKey("performanceImpact", performance_impact, archive_in); + parseAppsecJSONKey("lastUpdate", last_update, archive_in); + parseAppsecJSONKey("maintrainId", maintrain_id, archive_in); + parseAppsecJSONKey>("tags", tags, archive_in); + parseAppsecJSONKey>("cveList", cve_list, archive_in); + +} + +void +ProtectionMetadata::save(cereal::JSONOutputArchive &out_ar) const +{ + out_ar( + cereal::make_nvp("protectionName", protection_name), + cereal::make_nvp("severity", severity), + cereal::make_nvp("confidenceLevel", confidence_level), + cereal::make_nvp("performanceImpact", performance_impact), + cereal::make_nvp("lastUpdate", last_update), + cereal::make_nvp("maintrainId", maintrain_id), + cereal::make_nvp("tags", tags), + cereal::make_nvp("cveList", cve_list), + cereal::make_nvp("silent", silent) + ); +} + +ProtectionsProtectionsSection::ProtectionsProtectionsSection( + const ProtectionMetadata &_protection_metadata, + const DetectionRules &_detection_rules) + : + protection_metadata(_protection_metadata), + detection_rules(_detection_rules) +{ +} + +void +ProtectionsProtectionsSection::load(cereal::JSONInputArchive &archive_in) +{ + dbgTrace(D_LOCAL_POLICY) << "Loading Snort protections protections section"; + parseAppsecJSONKey("protectionMetadata", protection_metadata, archive_in); + parseAppsecJSONKey("detectionRules", detection_rules, archive_in); +} + +void +ProtectionsProtectionsSection::save(cereal::JSONOutputArchive &out_ar) const +{ + out_ar( + cereal::make_nvp("protectionMetadata", protection_metadata), + cereal::make_nvp("detectionRules", detection_rules) + ); +} + +ProtectionsSection::ProtectionsSection( + const std::vector &_protections, + const std::string &_name, + const std::string &_modification_time) + : + protections(_protections), + name(_name), + modification_time(_modification_time) +{ +} + +void +ProtectionsSection::load(cereal::JSONInputArchive &archive_in) +{ + dbgTrace(D_LOCAL_POLICY) << "Loading Snort protections section"; + parseAppsecJSONKey>("protections", protections, archive_in); +} + +void +ProtectionsSection::save(cereal::JSONOutputArchive &out_ar) const +{ + out_ar( + cereal::make_nvp("name", name), + cereal::make_nvp("modificationTime", modification_time), + cereal::make_nvp("protections", protections) + ); +} + +const vector & +ProtectionsSection::getProtections() const +{ + return protections; +} + +void +ProtectionsSectionWrapper::serialize(cereal::JSONInputArchive &archive_in) +{ + dbgTrace(D_LOCAL_POLICY) << "Loading Snort Section"; + parseAppsecJSONKey("IPSSnortSigs", protections, archive_in); +} + +const vector & +ProtectionsSectionWrapper::getProtections() const +{ + return protections.getProtections(); +} + +void +SnortSection::save(cereal::JSONOutputArchive &out_ar) const +{ + string version = "LocalVersion"; + out_ar( + cereal::make_nvp("VersionId", version), + cereal::make_nvp("SnortProtections", snort_protections), + cereal::make_nvp("protections", protections) + ); +} + +void +SnortSectionWrapper::save(cereal::JSONOutputArchive &out_ar) const +{ + out_ar( + cereal::make_nvp("IPSSnortSigs", snort) + ); +} + void NewSnortSignaturesAndOpenSchemaAPI::load(cereal::JSONInputArchive &archive_in) { dbgTrace(D_LOCAL_POLICY) << "Loading AppSec Snort Signatures practice"; - parseAppsecJSONKey("overrideMode", override_mode, archive_in, "Inactive"); + parseAppsecJSONKey("overrideMode", override_mode, archive_in, "inactive"); parseAppsecJSONKey>("configmap", config_map, archive_in); if (valid_modes.count(override_mode) == 0) { dbgWarning(D_LOCAL_POLICY) << "AppSec Snort Signatures override mode invalid: " << override_mode; } } +void +NewSnortSignaturesAndOpenSchemaAPI::addFile(const string &file_name) +{ + files.push_back(file_name); +} + const string & NewSnortSignaturesAndOpenSchemaAPI::getOverrideMode() const { return override_mode; } +const vector & +NewSnortSignaturesAndOpenSchemaAPI::getFiles() const +{ + return files; +} + const vector & NewSnortSignaturesAndOpenSchemaAPI::getConfigMap() const { @@ -320,7 +550,7 @@ void NewIntrusionPrevention::load(cereal::JSONInputArchive &archive_in) { dbgTrace(D_LOCAL_POLICY) << "Loading AppSec Intrusion Prevention practice"; - parseAppsecJSONKey("overrideMode", override_mode, archive_in, "Inactive"); + parseAppsecJSONKey("overrideMode", override_mode, archive_in, "inactive"); if (valid_modes.count(override_mode) == 0) { dbgWarning(D_LOCAL_POLICY) << "AppSec Intrusion Prevention override mode invalid: " << override_mode; } @@ -596,7 +826,7 @@ void NewFileSecurity::load(cereal::JSONInputArchive &archive_in) { dbgTrace(D_LOCAL_POLICY) << "Loading AppSec File Security practice"; - parseAppsecJSONKey("overrideMode", override_mode, archive_in, "Inactive"); + parseAppsecJSONKey("overrideMode", override_mode, archive_in, "inactive"); if (valid_modes.count(override_mode) == 0) { dbgWarning(D_LOCAL_POLICY) << "AppSec File Security override mode invalid: " << override_mode; } @@ -633,6 +863,11 @@ NewFileSecurity::load(cereal::JSONInputArchive &archive_in) parseAppsecJSONKey("largeFileInspection", large_file_inspection, archive_in); } +const string & +NewFileSecurity::getOverrideMode() const +{ + return override_mode; +} const NewFileSecurityArchiveInspection & NewFileSecurity::getArchiveInspection() const @@ -707,8 +942,8 @@ NewAppSecPracticeSpec::getOpenSchemaValidation() const return openapi_schema_validation; } -const NewSnortSignaturesAndOpenSchemaAPI & -NewAppSecPracticeSpec::getSnortSignatures() const +NewSnortSignaturesAndOpenSchemaAPI & +NewAppSecPracticeSpec::getSnortSignatures() { return snort_signatures; } diff --git a/components/security_apps/orchestration/local_policy_mgmt_gen/new_trusted_sources.cc b/components/security_apps/local_policy_mgmt_gen/new_trusted_sources.cc old mode 100644 new mode 100755 similarity index 100% rename from components/security_apps/orchestration/local_policy_mgmt_gen/new_trusted_sources.cc rename to components/security_apps/local_policy_mgmt_gen/new_trusted_sources.cc diff --git a/components/security_apps/orchestration/local_policy_mgmt_gen/policy_maker_utils.cc b/components/security_apps/local_policy_mgmt_gen/policy_maker_utils.cc old mode 100644 new mode 100755 similarity index 90% rename from components/security_apps/orchestration/local_policy_mgmt_gen/policy_maker_utils.cc rename to components/security_apps/local_policy_mgmt_gen/policy_maker_utils.cc index 11fa9fc..9aea4b7 --- a/components/security_apps/orchestration/local_policy_mgmt_gen/policy_maker_utils.cc +++ b/components/security_apps/local_policy_mgmt_gen/policy_maker_utils.cc @@ -27,6 +27,7 @@ SecurityAppsWrapper::save(cereal::JSONOutputArchive &out_ar) const cereal::make_nvp("rules", rules), cereal::make_nvp("ips", ips), cereal::make_nvp("exceptions", exceptions), + cereal::make_nvp("snort", snort), cereal::make_nvp("fileSecurity", file_security), cereal::make_nvp("version", policy_version) ); @@ -53,29 +54,30 @@ PolicyMakerUtils::getPolicyName(const string &policy_path) return policy_name; } -Maybe -PolicyMakerUtils::openPolicyAsJson(const string &policy_path) +template +Maybe +PolicyMakerUtils::openFileAsJson(const string &path) { - auto maybe_policy_as_json = Singleton::Consume::by()->getExecOutput( - getFilesystemPathConfig() + "/bin/yq " + policy_path + " -o json" + auto maybe_file_as_json = Singleton::Consume::by()->getExecOutput( + getFilesystemPathConfig() + "/bin/yq " + path + " -o json" ); - if (!maybe_policy_as_json.ok()) { + if (!maybe_file_as_json.ok()) { dbgDebug(D_NGINX_POLICY) << "Could not convert policy from yaml to json"; - return genError("Could not convert policy from yaml to json. Error: " + maybe_policy_as_json.getErr()); + return genError("Could not convert policy from yaml to json. Error: " + maybe_file_as_json.getErr()); } auto i_orchestration_tools = Singleton::Consume::by(); - auto maybe_policy = i_orchestration_tools->jsonStringToObject( - maybe_policy_as_json.unpack() + auto maybe_file = i_orchestration_tools->jsonStringToObject( + maybe_file_as_json.unpack() ); - if (!maybe_policy.ok()) { - string error = "Policy in path: " + policy_path + " was not loaded. Error: " + maybe_policy.getErr(); + if (!maybe_file.ok()) { + string error = "Policy in path: " + path + " was not loaded. Error: " + maybe_file.getErr(); dbgDebug(D_NGINX_POLICY) << error; return genError(error); } - return maybe_policy.unpack(); + return maybe_file.unpack(); } void @@ -86,6 +88,11 @@ PolicyMakerUtils::clearElementsMaps() inner_exceptions.clear(); web_apps.clear(); rules_config.clear(); + ips.clear(); + snort.clear(); + snort_protections.clear(); + file_security.clear(); + rate_limit.clear(); } // LCOV_EXCL_START Reason: no test exist - needed for NGINX config @@ -351,6 +358,19 @@ convertMapToVector(map map) return vec; } +vector +convertExceptionsMapToVector(map> map) +{ + vector vec; + if (map.empty()) { + return vec; + } + for (const auto &m : map) { + if (!m.first.empty()) vec.insert(vec.end(), m.second.begin(), m.second.end()); + } + return vec; +} + template R getAppsecPracticeSpec(const string &practice_annotation_name, const T &policy) @@ -404,7 +424,7 @@ template R getAppsecExceptionSpec(const string &exception_annotation_name, const T &policy) { - auto exceptions_vec = policy.getAppsecExceptionSpecs(); + auto exceptions_vec = policy.getAppsecExceptions(); auto exception_it = extractElement(exceptions_vec.begin(), exceptions_vec.end(), exception_annotation_name); if (exception_it == exceptions_vec.end()) { @@ -710,26 +730,24 @@ createTrustedSourcesSection( } template -InnerException +vector createExceptionSection( const string &exception_annotation_name, const T &policy) { - AppsecExceptionSpec exception_spec = - getAppsecExceptionSpec(exception_annotation_name, policy); - ExceptionMatch exception_match(exception_spec); - string behavior = - exception_spec.getAction() == "skip" ? - "ignore" : - exception_spec.getAction(); - - ExceptionBehavior exception_behavior("action", behavior); - InnerException inner_exception(exception_behavior, exception_match); - return inner_exception; + AppsecException exception_spec = + getAppsecExceptionSpec(exception_annotation_name, policy); + vector res; + for (auto exception : exception_spec.getExceptions()) { + ExceptionMatch exception_match(exception); + ExceptionBehavior exception_behavior(exception.getAction()); + res.push_back(InnerException(exception_behavior, exception_match)); + } + return res; } template<> -InnerException +vector createExceptionSection( const string &exception_annotation_name, const V1beta2AppsecLinuxPolicy &policy) @@ -737,14 +755,9 @@ createExceptionSection( NewAppsecException exception_spec = getAppsecExceptionSpec(exception_annotation_name, policy); ExceptionMatch exception_match(exception_spec); - string behavior = - exception_spec.getAction() == "skip" ? - "ignore" : - exception_spec.getAction(); - - ExceptionBehavior exception_behavior("action", behavior); + ExceptionBehavior exception_behavior(exception_spec.getAction()); InnerException inner_exception(exception_behavior, exception_match); - return inner_exception; + return {inner_exception}; } template @@ -842,10 +855,13 @@ createMultiRulesSections( const string &web_user_res_vec_type, const string &asset_name, const string &exception_name, - const string &exception_id) + const vector &exceptions) { PracticeSection practice = PracticeSection(practice_id, practice_type, practice_name); - ParametersSection exception_param = ParametersSection(exception_id, exception_name); + vector exceptions_result; + for (auto exception : exceptions) { + exceptions_result.push_back(ParametersSection(exception.getBehaviorId(), exception_name)); + } vector triggers; if (!log_trigger_id.empty()) { @@ -864,7 +880,7 @@ createMultiRulesSections( url, uri, {practice}, - {exception_param}, + exceptions_result, triggers ); @@ -889,9 +905,9 @@ createMultiRulesSections( const string &web_user_res_vec_type, const string &asset_name, const string &exception_name, - const string &exception_id) + const vector &exceptions) { - ParametersSection exception_param = ParametersSection(exception_id, exception_name); + ParametersSection exception_param = ParametersSection(exceptions[0].getBehaviorId(), exception_name); vector practices; if (!practice_id.empty()) { @@ -941,6 +957,9 @@ PolicyMakerUtils::createIpsSections( auto apssec_practice = getAppsecPracticeSpec( rule_annotations[AnnotationTypes::PRACTICE], policy); + + if (apssec_practice.getIntrusionPrevention().getMode().empty()) return; + IpsProtectionsSection ips_section = IpsProtectionsSection( context, asset_name, @@ -955,6 +974,74 @@ PolicyMakerUtils::createIpsSections( ips[asset_name] = ips_section; } +void +PolicyMakerUtils::createSnortProtecionsSection(const string &file_name, const string &practice_name) +{ + auto path = getFilesystemPathConfig() + "/conf/snort/snort_k8s_" + practice_name; + if (snort_protections.find(path) != snort_protections.end()) return; + + auto snort_scriipt_path = getFilesystemPathConfig() + "/scripts/snort_to_ips_local.py"; + auto cmd = "python " + snort_scriipt_path + " " + path + ".rule " + path + ".out " + path + ".err"; + + auto res = Singleton::Consume::by()->getExecOutput(cmd); + + if (!res.ok()) { + dbgWarning(D_LOCAL_POLICY) << res.getErr(); + return; + } + + Maybe maybe_protections = openFileAsJson(path + ".out"); + if (!maybe_protections.ok()){ + dbgWarning(D_LOCAL_POLICY) << maybe_protections.getErr(); + return; + } + + auto i_orchestration_tools = Singleton::Consume::by(); + i_orchestration_tools->removeFile(path + ".rule"); + i_orchestration_tools->removeFile(path + ".out"); + i_orchestration_tools->removeFile(path + ".err"); + + snort_protections[path] = ProtectionsSection( + maybe_protections.unpack().getProtections(), + file_name + ); +} + +void +PolicyMakerUtils::createSnortSections( + const string & context, + const string &asset_name, + const string &asset_id, + const string &practice_name, + const string &practice_id, + const string &source_identifier, + const V1beta2AppsecLinuxPolicy &policy, + map &rule_annotations) +{ + auto apssec_practice = getAppsecPracticeSpec( + rule_annotations[AnnotationTypes::PRACTICE], + policy); + + if (apssec_practice.getSnortSignatures().getOverrideMode() == "inactive" || + apssec_practice.getSnortSignatures().getFiles().size() == 0) { + return; + } + createSnortProtecionsSection(apssec_practice.getSnortSignatures().getFiles()[0], apssec_practice.getName()); + + SnortProtectionsSection snort_section = SnortProtectionsSection( + context, + asset_name, + asset_id, + practice_name, + practice_id, + source_identifier, + apssec_practice.getSnortSignatures().getOverrideMode(), + apssec_practice.getSnortSignatures().getFiles() + ); + + snort[asset_name] = snort_section; +} + void PolicyMakerUtils::createFileSecuritySections( const string &asset_id, @@ -968,6 +1055,9 @@ PolicyMakerUtils::createFileSecuritySections( auto apssec_practice = getAppsecPracticeSpec( rule_annotations[AnnotationTypes::PRACTICE], policy); + + if (apssec_practice.getFileSecurity().getOverrideMode().empty()) return; + auto file_security_section = apssec_practice.getFileSecurity().createFileSecurityProtectionsSection( context, asset_name, @@ -1095,7 +1185,7 @@ PolicyMakerUtils::createThreatPreventionPracticeSections( "WebUserResponse", asset_name, rule_annotations[AnnotationTypes::EXCEPTION], - inner_exceptions[rule_annotations[AnnotationTypes::EXCEPTION]].getBehaviorId() + inner_exceptions[rule_annotations[AnnotationTypes::EXCEPTION]] ); rules_config[rule_config.getAssetName()] = rule_config; @@ -1121,6 +1211,17 @@ PolicyMakerUtils::createThreatPreventionPracticeSections( rule_annotations ); + createSnortSections( + "practiceId(" + practice_id + ")", + rule_config.getAssetName(), + rule_config.getAssetId(), + rule_annotations[AnnotationTypes::PRACTICE], + practice_id, + current_identifier, + policy, + rule_annotations + ); + createFileSecuritySections( rule_config.getAssetId(), rule_config.getAssetName(), @@ -1158,12 +1259,13 @@ PolicyMakerUtils::combineElementsToPolicy(const string &policy_version) ) ); ExceptionsWrapper exceptions_section({ - ExceptionsRulebase(convertMapToVector(inner_exceptions)) + ExceptionsRulebase(convertExceptionsMapToVector(inner_exceptions)) }); AppSecWrapper appses_section(AppSecRulebase(convertMapToVector(web_apps), {})); RulesConfigWrapper rules_config_section(convertMapToVector(rules_config), convertMapToVector(users_identifiers)); IntrusionPreventionWrapper ips_section(convertMapToVector(ips)); + SnortSectionWrapper snort_section(convertMapToVector(snort), convertMapToVector(snort_protections)); FileSecurityWrapper file_security_section(convertMapToVector(file_security)); AccessControlRulebaseWrapper rate_limit_section(convertMapToVector(rate_limit)); SecurityAppsWrapper security_app_section = SecurityAppsWrapper( @@ -1171,6 +1273,7 @@ PolicyMakerUtils::combineElementsToPolicy(const string &policy_version) triggers_section, rules_config_section, ips_section, + snort_section, rate_limit_section, file_security_section, exceptions_section, @@ -1277,7 +1380,7 @@ PolicyMakerUtils::createPolicyElementsByRule( "WebUserResponse", full_url, rule_annotations[AnnotationTypes::EXCEPTION], - inner_exceptions[rule_annotations[AnnotationTypes::EXCEPTION]].getBehaviorId() + inner_exceptions[rule_annotations[AnnotationTypes::EXCEPTION]] ); rules_config[rule_config.getAssetName()] = rule_config; @@ -1303,7 +1406,8 @@ PolicyMakerUtils::createPolicyElementsByRule( getAppsecPracticeSpec(rule_annotations[AnnotationTypes::PRACTICE], policy), log_triggers[rule_annotations[AnnotationTypes::TRIGGER]], rule.getMode(), - trusted_sources[rule_annotations[AnnotationTypes::TRUSTED_SOURCES]] + trusted_sources[rule_annotations[AnnotationTypes::TRUSTED_SOURCES]], + inner_exceptions[rule_annotations[AnnotationTypes::EXCEPTION]] ); web_apps[rule_config.getAssetName()] = web_app; } @@ -1468,7 +1572,7 @@ PolicyMakerUtils::proccesSingleAppsecPolicy( const string &policy_version, const string &local_appsec_policy_path) { - Maybe maybe_policy = openPolicyAsJson(policy_path); + Maybe maybe_policy = openFileAsJson(policy_path); if (!maybe_policy.ok()){ dbgWarning(D_LOCAL_POLICY) << maybe_policy.getErr(); return ""; diff --git a/components/security_apps/orchestration/local_policy_mgmt_gen/rules_config_section.cc b/components/security_apps/local_policy_mgmt_gen/rules_config_section.cc old mode 100644 new mode 100755 similarity index 99% rename from components/security_apps/orchestration/local_policy_mgmt_gen/rules_config_section.cc rename to components/security_apps/local_policy_mgmt_gen/rules_config_section.cc index 5f748a5..1ffe81d --- a/components/security_apps/orchestration/local_policy_mgmt_gen/rules_config_section.cc +++ b/components/security_apps/local_policy_mgmt_gen/rules_config_section.cc @@ -98,6 +98,7 @@ PracticeSection::save(cereal::JSONOutputArchive &out_ar) const ); } +// LCOV_EXCL_START Reason: no test exist ParametersSection::ParametersSection( const string &_id, const string &_name) @@ -120,6 +121,7 @@ ParametersSection::save(cereal::JSONOutputArchive &out_ar) const cereal::make_nvp("parameterType", type) ); } +// LCOV_EXCL_STOP RulesTriggerSection::RulesTriggerSection( const string &_name, diff --git a/components/security_apps/orchestration/local_policy_mgmt_gen/settings_section.cc b/components/security_apps/local_policy_mgmt_gen/settings_section.cc old mode 100644 new mode 100755 similarity index 100% rename from components/security_apps/orchestration/local_policy_mgmt_gen/settings_section.cc rename to components/security_apps/local_policy_mgmt_gen/settings_section.cc diff --git a/components/security_apps/orchestration/local_policy_mgmt_gen/snort_section.cc b/components/security_apps/local_policy_mgmt_gen/snort_section.cc old mode 100644 new mode 100755 similarity index 100% rename from components/security_apps/orchestration/local_policy_mgmt_gen/snort_section.cc rename to components/security_apps/local_policy_mgmt_gen/snort_section.cc diff --git a/components/security_apps/orchestration/local_policy_mgmt_gen/triggers_section.cc b/components/security_apps/local_policy_mgmt_gen/triggers_section.cc old mode 100644 new mode 100755 similarity index 100% rename from components/security_apps/orchestration/local_policy_mgmt_gen/triggers_section.cc rename to components/security_apps/local_policy_mgmt_gen/triggers_section.cc diff --git a/components/security_apps/orchestration/local_policy_mgmt_gen/trusted_sources_section.cc b/components/security_apps/local_policy_mgmt_gen/trusted_sources_section.cc old mode 100644 new mode 100755 similarity index 100% rename from components/security_apps/orchestration/local_policy_mgmt_gen/trusted_sources_section.cc rename to components/security_apps/local_policy_mgmt_gen/trusted_sources_section.cc diff --git a/components/security_apps/orchestration/CMakeLists.txt b/components/security_apps/orchestration/CMakeLists.txt index fc76575..4c500e8 100755 --- a/components/security_apps/orchestration/CMakeLists.txt +++ b/components/security_apps/orchestration/CMakeLists.txt @@ -12,7 +12,6 @@ add_subdirectory(manifest_controller) add_subdirectory(update_communication) add_subdirectory(details_resolver) add_subdirectory(health_check) -add_subdirectory(local_policy_mgmt_gen) add_subdirectory(env_details) #add_subdirectory(orchestration_ut) diff --git a/components/security_apps/orchestration/details_resolver/details_resolver_handlers/checkpoint_product_handlers.h b/components/security_apps/orchestration/details_resolver/details_resolver_handlers/checkpoint_product_handlers.h index 97958c1..5a36ff8 100755 --- a/components/security_apps/orchestration/details_resolver/details_resolver_handlers/checkpoint_product_handlers.h +++ b/components/security_apps/orchestration/details_resolver/details_resolver_handlers/checkpoint_product_handlers.h @@ -42,6 +42,16 @@ checkSamlPortal(const string &command_output) return genError("Current host does not have SAML Portal configured"); } +Maybe +getIDAGaia(const string &command_output) +{ + if (command_output.find("Portal is running") != string::npos) { + return string("ida_gaia"); + } + + return genError("Current host does not have SAML Portal configured"); +} + Maybe checkIDP(shared_ptr file_stream) { @@ -226,58 +236,24 @@ getSmbGWIPSecVPNBlade(const string &command_output) { return getSmbBlade(command_output, "IPSec VPN Blade was not found"); } - -Maybe -getMgmtParentObjAttr(shared_ptr file_stream, const string &parent_obj, const string &attr) -{ - string line; - bool found_parent_obj = false; - while (getline(*file_stream, line)) { - size_t parent_obj_pos = line.find(parent_obj); - if (parent_obj_pos != string::npos) found_parent_obj = true; - if (!found_parent_obj) continue; - - size_t attr_pos = line.find(attr); - if (attr_pos == string::npos) continue; - line = line.substr(attr_pos + attr.size()); - return line; - } - return genError("Parent object attribute was not found. Attr: " + attr); -} #endif // gaia || smb #if defined(gaia) Maybe -getMgmtParentObjUid(shared_ptr file_stream) +getMgmtParentObjUid(const string &command_output) { - auto maybe_unparsed_uid = getMgmtParentObjAttr(file_stream, "cluster_object", "Uid "); - if (!maybe_unparsed_uid.ok()) { - return maybe_unparsed_uid; - } - const string &unparsed_uid = maybe_unparsed_uid.unpack(); - auto maybe_uid = chopHeadAndTail(unparsed_uid, "(\"{", "}\")"); - if (!maybe_uid.ok()) { - return maybe_uid; - } - string uid = maybe_uid.unpack(); - transform(uid.begin(), uid.end(), uid.begin(), ::tolower); - return uid; + return getAttr(command_output, "Parent object uuid was not found"); } Maybe -getMgmtParentObjName(shared_ptr file_stream) +getMgmtParentObjName(const string &command_output) { - auto maybe_unparsed_name = getMgmtParentObjAttr(file_stream, "cluster_object", "Name "); - if (!maybe_unparsed_name.ok()) { - return maybe_unparsed_name; - } - const string &unparsed_name = maybe_unparsed_name.unpack(); - return chopHeadAndTail(unparsed_name, "(", ")"); + return getAttr(command_output, "Parent object name was not found"); } #elif defined(smb) Maybe -getMgmtParentObjUid(const string &command_output) +getSmbMgmtParentObjUid(const string &command_output) { if (!command_output.empty()) { return command_output; @@ -286,7 +262,7 @@ getMgmtParentObjUid(const string &command_output) } Maybe -getMgmtParentObjName(const string &command_output) +getSmbMgmtParentObjName(const string &command_output) { if (!command_output.empty()) { return command_output; @@ -314,6 +290,34 @@ getOsRelease(shared_ptr file_stream) return genError("Os release was not found"); } +Maybe +getWaapModelVersion(shared_ptr file_stream) +{ + string line; + static const int max_lines = 5; + int i = 0; + bool found_key = false; + while (i < max_lines && getline(*file_stream, line)) { + if (!found_key) { + size_t index = line.find("\"model_version\":"); + if (index != string::npos) { + found_key = true; + } + } else { + size_t start = line.find_first_of('"'); + size_t end = line.find_last_of('"'); + if (start != string::npos && end != string::npos && end > start) { + return line.substr(start + 1, end - start - 1); + } else { + return genError("Model version value unreadable"); + } + } + i++; + } + + return genError("Model version was not found"); +} + #if defined(alpine) string & ltrim(string &s) diff --git a/components/security_apps/orchestration/details_resolver/details_resolver_handlers/details_resolver_impl.h b/components/security_apps/orchestration/details_resolver/details_resolver_handlers/details_resolver_impl.h index e979501..7beed3d 100755 --- a/components/security_apps/orchestration/details_resolver/details_resolver_handlers/details_resolver_impl.h +++ b/components/security_apps/orchestration/details_resolver/details_resolver_handlers/details_resolver_impl.h @@ -55,6 +55,19 @@ SHELL_CMD_HANDLER( #if defined(gaia) SHELL_CMD_HANDLER("hasSupportedBlade", "enabled_blades", checkHasSupportedBlade) SHELL_CMD_HANDLER("hasSamlPortal", "mpclient status saml-vpn", checkSamlPortal) +SHELL_CMD_HANDLER("requiredNanoServices", "mpclient status saml-vpn", getIDAGaia) +SHELL_CMD_HANDLER( + "cpProductIntegrationMgmtParentObjectName", + "cat $FWDIR/database/myself_objects.C " + "| awk -F '[:()]' '/:cluster_object/ {found=1; next} found && /:Name/ {print $3; exit}'", + getMgmtParentObjName +) +SHELL_CMD_HANDLER( + "cpProductIntegrationMgmtParentObjectUid", + "cat $FWDIR/database/myself_objects.C " + "| awk -F'[{}]' '/:cluster_object/ { found=1; next } found && /:Uid/ { uid=tolower($2); print uid; exit }'", + getMgmtParentObjUid +) SHELL_CMD_HANDLER( "Hardware", "cat $FWDIR/database/myself_objects.C | awk -F '[:()]' '/:appliance_type/ {print $3}' | head -n 1", @@ -81,12 +94,12 @@ SHELL_CMD_HANDLER( SHELL_CMD_HANDLER( "cpProductIntegrationMgmtParentObjectName", "cpsdwan get_data | jq -r .cluster_name", - getMgmtParentObjName + getSmbMgmtParentObjName ) SHELL_CMD_HANDLER( "cpProductIntegrationMgmtParentObjectUid", "cpsdwan get_data | jq -r .cluster_uuid", - getMgmtParentObjUid + getSmbMgmtParentObjUid ) SHELL_CMD_HANDLER( "cpProductIntegrationMgmtObjectName", @@ -143,4 +156,6 @@ FILE_CONTENT_HANDLER( FILE_CONTENT_HANDLER("os_release", "/etc/os-release", getOsRelease) #endif // gaia || smb +FILE_CONTENT_HANDLER("AppSecModelVersion", "/etc/cp/conf/waap/waap.data", getWaapModelVersion) + #endif // FILE_CONTENT_HANDLER diff --git a/components/security_apps/orchestration/details_resolver/details_resolving_handler.cc b/components/security_apps/orchestration/details_resolver/details_resolving_handler.cc index b8e0cbf..33046cc 100755 --- a/components/security_apps/orchestration/details_resolver/details_resolving_handler.cc +++ b/components/security_apps/orchestration/details_resolver/details_resolving_handler.cc @@ -22,6 +22,7 @@ #include "maybe_res.h" #include "enum_array.h" #include "i_shell_cmd.h" +#include "i_orchestration_tools.h" #include "config.h" using namespace std; @@ -77,7 +78,8 @@ DetailsResolvingHanlder::Impl::getResolvedDetails() const const string &path = file_handler.second.first; FileContentHandler handler = file_handler.second.second; - shared_ptr in_file = make_shared(path); + shared_ptr in_file = + Singleton::Consume::by()->fileStreamWrapper(path); if (!in_file->is_open()) { dbgWarning(D_AGENT_DETAILS) << "Could not open file for processing. Path: " << path; continue; diff --git a/components/security_apps/orchestration/details_resolver/details_resolving_handler.h b/components/security_apps/orchestration/details_resolver/details_resolving_handler.h index 7ffe122..0e70bce 100755 --- a/components/security_apps/orchestration/details_resolver/details_resolving_handler.h +++ b/components/security_apps/orchestration/details_resolver/details_resolving_handler.h @@ -18,11 +18,13 @@ #include #include "i_shell_cmd.h" +#include "i_orchestration_tools.h" #include "i_agent_details_reporter.h" class DetailsResolvingHanlder : Singleton::Consume, + Singleton::Consume, Singleton::Consume { public: diff --git a/components/security_apps/orchestration/downloader/curl_client.cc b/components/security_apps/orchestration/downloader/curl_client.cc index 2c9fb41..dce531a 100755 --- a/components/security_apps/orchestration/downloader/curl_client.cc +++ b/components/security_apps/orchestration/downloader/curl_client.cc @@ -278,6 +278,36 @@ HttpsCurl::HttpsCurl(const HttpsCurl &other) : HttpCurl(other), ca_path(other.ca_path) {} +bool +HttpsCurl::downloadOpenAppsecPackages() +{ + char errorstr[CURL_ERROR_SIZE]; + CURL* curl_handle = curl_easy_init(); + if (!curl_handle) return false; + + curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 1); + curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 2); + + curl_easy_setopt(curl_handle, CURLOPT_URL, ("https://" + curl_url).c_str()); + curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, writeResponseCallback); + curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, &out_file); + + curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L); + curl_easy_setopt(curl_handle, CURLOPT_ERRORBUFFER, errorstr); + + CURLcode res = curl_easy_perform(curl_handle); + if (res == CURLE_OK) { + dbgTrace(D_HTTP_REQUEST) << "CURL HTTP request successfully completed."; + } else { + dbgWarning(D_HTTP_REQUEST) << "CURL result " + string(curl_easy_strerror(res)); + curl_easy_cleanup(curl_handle); + return false; + } + + curl_easy_cleanup(curl_handle); + return true; +} + void HttpsCurl::setCurlOpts(long timeout, HTTP_VERSION http_version) { @@ -299,9 +329,9 @@ HttpsCurl::setCurlOpts(long timeout, HTTP_VERSION http_version) curl_easy_setopt(curl_handle, CURLOPT_HTTP_VERSION, http_version); //SSL options - if (getProfileAgentSettingWithDefault( - false, - "agent.config.message.ignoreSslValidation") == false) + if ( + getProfileAgentSettingWithDefault(false, "agent.config.message.ignoreSslValidation") == false + ) { curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 1L); curl_easy_setopt(curl_handle, CURLOPT_SSL_CTX_FUNCTION, ssl_ctx_verify_certificate); diff --git a/components/security_apps/orchestration/downloader/curl_client.h b/components/security_apps/orchestration/downloader/curl_client.h index 9104f29..fd99dd8 100755 --- a/components/security_apps/orchestration/downloader/curl_client.h +++ b/components/security_apps/orchestration/downloader/curl_client.h @@ -105,6 +105,7 @@ public: static CURLcode ssl_ctx_verify_certificate(CURL *curl, void *ssl_ctx, void *opq); static int verify_certificate(int preverify_ok, X509_STORE_CTX *ctx); void setCurlOpts(long timeout = 60L, HTTP_VERSION http_version = HTTP_VERSION::HTTP_VERSION_1_1) override; + bool downloadOpenAppsecPackages(); private: std::string ca_path; diff --git a/components/security_apps/orchestration/downloader/downloader_ut/downloader_ut.cc b/components/security_apps/orchestration/downloader/downloader_ut/downloader_ut.cc index 14c6721..df7fa0b 100755 --- a/components/security_apps/orchestration/downloader/downloader_ut/downloader_ut.cc +++ b/components/security_apps/orchestration/downloader/downloader_ut/downloader_ut.cc @@ -51,7 +51,7 @@ TEST_F(DownloaderTest, downloadFileFromFog) calculateChecksum(Package::ChecksumTypes::SHA256, "/tmp/virtualSettings.download") ).WillOnce(Return(string("123"))); - EXPECT_CALL(mock_orchestration_tools, writeFile(fog_response, "/tmp/virtualSettings.download")) + EXPECT_CALL(mock_orchestration_tools, writeFile(fog_response, "/tmp/virtualSettings.download", false)) .WillOnce(Return(true)); EXPECT_CALL(mock_orchestration_tools, isNonEmptyFile("/tmp/virtualSettings.download")).WillOnce(Return(true)); @@ -183,7 +183,7 @@ TEST_F(DownloaderTest, downloadEmptyFileFromFog) EXPECT_CALL(mock_communication, downloadAttributeFile(resourse_file)).WillOnce(Return(fog_response)); - EXPECT_CALL(mock_orchestration_tools, writeFile(fog_response, "/tmp/manifest.download")) + EXPECT_CALL(mock_orchestration_tools, writeFile(fog_response, "/tmp/manifest.download", false)) .WillOnce(Return(true)); EXPECT_CALL(mock_orchestration_tools, isNonEmptyFile("/tmp/manifest.download")).WillOnce(Return(false)); @@ -342,13 +342,23 @@ TEST_F(DownloaderTest, download_virtual_policy) EXPECT_CALL(mock_communication, downloadAttributeFile(resourse_file)).WillOnce(Return(fog_response)); - EXPECT_CALL(mock_orchestration_tools, writeFile(tenant_0000_file, "/tmp/virtualPolicy_0000_profile_1234.download")) - .WillOnce(Return(true)); + EXPECT_CALL( + mock_orchestration_tools, + writeFile( + tenant_0000_file, + "/tmp/virtualPolicy_0000_profile_1234.download", + false) + ).WillOnce(Return(true)); EXPECT_CALL(mock_orchestration_tools, fillKeyInJson(_, _, _)).WillRepeatedly(Return()); - EXPECT_CALL(mock_orchestration_tools, writeFile(tenant_1111_file, "/tmp/virtualPolicy_1111_profile_1235.download")) - .WillOnce(Return(true)); + EXPECT_CALL( + mock_orchestration_tools, + writeFile( + tenant_1111_file, + "/tmp/virtualPolicy_1111_profile_1235.download", + false) + ).WillOnce(Return(true)); map, string> expected_downloaded_files = { @@ -427,7 +437,8 @@ TEST_F(DownloaderTest, download_virtual_settings) mock_orchestration_tools, writeFile( tenant_0000_file, - tenant_0000_path.str() + tenant_0000_path.str(), + false ) ).WillOnce(Return(true)); diff --git a/components/security_apps/orchestration/downloader/http_client.h b/components/security_apps/orchestration/downloader/http_client.h index ae725f9..68a4bb0 100755 --- a/components/security_apps/orchestration/downloader/http_client.h +++ b/components/security_apps/orchestration/downloader/http_client.h @@ -37,8 +37,8 @@ private: std::string loadCAChainDir(); Maybe getFileSSL(const URLParser &url, std::ofstream &out_file, const std::string &_token); Maybe getFileHttp(const URLParser &url, std::ofstream &out_file, const std::string &_token); - Maybe curlGetFileOverSSL(const URLParser &url, std::ofstream &out_file, const std::string &_token); Maybe curlGetFileOverHttp(const URLParser &url, std::ofstream &out_file, const std::string &_token); + Maybe curlGetFileOverSSL(const URLParser &url, std::ofstream &out_file, const std::string &_token); }; // LCOV_EXCL_STOP diff --git a/components/security_apps/orchestration/downloader/https_client.cc b/components/security_apps/orchestration/downloader/https_client.cc index 33d9031..8ef61a3 100755 --- a/components/security_apps/orchestration/downloader/https_client.cc +++ b/components/security_apps/orchestration/downloader/https_client.cc @@ -592,8 +592,13 @@ HTTPClient::curlGetFileOverSSL(const URLParser &url, ofstream &out_file, const s proxy_config->getProxyCredentials(ProxyProtocol::HTTPS), cert_file_path); - ssl_curl_client.setCurlOpts(); - bool connection_ok = ssl_curl_client.connect(); + bool connection_ok; + if (url.getBaseURL().unpack() == "downloads.openappsec.io") { + connection_ok = ssl_curl_client.downloadOpenAppsecPackages(); + } else { + ssl_curl_client.setCurlOpts(); + connection_ok = ssl_curl_client.connect(); + } if (!connection_ok) { stringstream url_s; diff --git a/components/security_apps/orchestration/health_check/health_check.cc b/components/security_apps/orchestration/health_check/health_check.cc index 96a43f0..db81a41 100755 --- a/components/security_apps/orchestration/health_check/health_check.cc +++ b/components/security_apps/orchestration/health_check/health_check.cc @@ -21,6 +21,7 @@ #include "config.h" #include "log_generator.h" #include "health_check_manager.h" +#include "agent_core_utilities.h" using namespace std; using namespace ReportIS; @@ -145,9 +146,11 @@ private: initCloudVendorConfig() { static const map> ip_port_defaults_map = { - {"Azure", make_pair("168.63.129.16", 8117)}, - {"Aws", make_pair("", 8117)} + {"Azure", make_pair(getenv("DOCKER_RPM_ENABLED") ? "" : "168.63.129.16", 8117)}, + {"Aws", make_pair("", 8117)}, + {"Local", make_pair("", 8117)} }; + auto cloud_vendor_maybe = getSetting("reverseProxy", "cloudVendorName"); if (cloud_vendor_maybe.ok()) { const string cloud_vendor = cloud_vendor_maybe.unpack(); @@ -247,13 +250,36 @@ private: ); } + HealthCheckStatus + getStandaloneHealthStatus() + { + if (!getenv("DOCKER_RPM_ENABLED")) return HealthCheckStatus::IGNORED; + + static const string standalone_cmd = "/usr/sbin/cpnano -s --docker-rpm; echo $?"; + dbgTrace(D_HEALTH_CHECK) << "Checking the standalone docker health status with command: " << standalone_cmd; + + auto maybe_result = Singleton::Consume::by()->getExecOutput(standalone_cmd, 1000); + if (!maybe_result.ok()) { + dbgWarning(D_HEALTH_CHECK) << "Unable to get the standalone docker status. Returning unhealthy status."; + return HealthCheckStatus::UNHEALTHY; + } + dbgTrace(D_HEALTH_CHECK) << "Got response: " << maybe_result.unpack(); + + auto response = NGEN::Strings::removeTrailingWhitespaces(maybe_result.unpack()); + + if (response.back() == '0') return HealthCheckStatus::HEALTHY; + if (response.back() == '1') return HealthCheckStatus::UNHEALTHY; + + return HealthCheckStatus::DEGRADED; + } + bool nginxContainerIsRunning() { static const string nginx_container_name = "cp_nginx_gaia"; static const string cmd_running = "docker ps --filter name=" + nginx_container_name + " --filter status=running"; - dbgTrace(D_HEALTH_CHECK) << "Checking if the container is running with the commmand: " << cmd_running; + dbgTrace(D_HEALTH_CHECK) << "Checking if the container is running with the command: " << cmd_running; auto maybe_result = Singleton::Consume::by()->getExecOutput(cmd_running); if (!maybe_result.ok()) { @@ -263,7 +289,6 @@ private: } return (*maybe_result).find(nginx_container_name) != string::npos; - } void @@ -279,7 +304,7 @@ private: { if (open_connections_counter >= max_connections) { dbgDebug(D_HEALTH_CHECK) - << "Cannot serve new client, reached maximun open connections bound which is:" + << "Cannot serve new client, reached maximum open connections bound which is:" << open_connections_counter << "maximum allowed: " << max_connections; @@ -331,6 +356,42 @@ private: "health check failed\r\n"; static const vector failure_response_buffer(failure_response.begin(), failure_response.end()); + static const string degraded_response = + "HTTP/1.1 202 OK\r\n" + "Content-Length: 22\r\n" + "Content-Type: text/plain\r\n" + "\r\n" + "health check partial\r\n"; + static const vector degraded_response_buffer(degraded_response.begin(), degraded_response.end()); + + HealthCheckStatus standalone_status = getStandaloneHealthStatus(); + if (standalone_status != HealthCheckStatus::IGNORED) { + if (standalone_status == HealthCheckStatus::HEALTHY) { + dbgDebug(D_HEALTH_CHECK) + << "Standalone status is healthy, returning the following response: " + << success_response; + i_socket->writeData(curr_client_socket, success_response_buffer); + closeCurrentSocket(curr_client_socket, curr_routine_id); + return; + } + + if (standalone_status == HealthCheckStatus::UNHEALTHY) { + dbgDebug(D_HEALTH_CHECK) + << "Standalone status in unhealthy, returning the following response: " + << failure_response; + i_socket->writeData(curr_client_socket, failure_response_buffer); + closeCurrentSocket(curr_client_socket, curr_routine_id); + return; + } + + dbgDebug(D_HEALTH_CHECK) + << "Standalone status was partially loaded, returning the following response: " + << degraded_response; + i_socket->writeData(curr_client_socket, degraded_response_buffer); + closeCurrentSocket(curr_client_socket, curr_routine_id); + return; + } + if (nginxContainerIsRunning()) { dbgDebug(D_HEALTH_CHECK) << "nginx conatiner is running, returning the following response: " diff --git a/components/security_apps/orchestration/health_check/health_check_ut/health_check_ut.cc b/components/security_apps/orchestration/health_check/health_check_ut/health_check_ut.cc index e6b4a69..c3c1396 100755 --- a/components/security_apps/orchestration/health_check/health_check_ut/health_check_ut.cc +++ b/components/security_apps/orchestration/health_check/health_check_ut/health_check_ut.cc @@ -194,7 +194,7 @@ TEST_F(HealthCheckerTest, connectionsLimit) connection_handler_routine(); EXPECT_THAT( - capture_debug.str(), HasSubstr("Cannot serve new client, reached maximun open connections") + capture_debug.str(), HasSubstr("Cannot serve new client, reached maximum open connections") ); } diff --git a/components/security_apps/orchestration/include/declarative_policy_utils.h b/components/security_apps/orchestration/include/declarative_policy_utils.h index c742124..461ba17 100644 --- a/components/security_apps/orchestration/include/declarative_policy_utils.h +++ b/components/security_apps/orchestration/include/declarative_policy_utils.h @@ -31,6 +31,14 @@ class ApplyPolicyEvent : public Event { public: ApplyPolicyEvent() {} + ApplyPolicyEvent(const std::string &path) : local_policy_path(path) {} + + // LCOV_EXCL_START Reason: no test exist + std::string getPolicyPath() const { return local_policy_path; } + // LCOV_EXCL_STOP + +private: + std::string local_policy_path; }; class DeclarativePolicyUtils @@ -40,6 +48,7 @@ class DeclarativePolicyUtils Singleton::Consume, Singleton::Consume, Singleton::Consume, + public Singleton::Consume, Singleton::Consume, public Listener { @@ -50,8 +59,7 @@ public: void doCall() override { - Singleton::Consume::by()->setPolicyPath(policy_path.get()); - ApplyPolicyEvent().notify(); + ApplyPolicyEvent(policy_path.get()).notify(); } private: @@ -80,6 +88,7 @@ public: private: std::string getCleanChecksum(const std::string &unclean_checksum); + std::string local_policy_path; std::string curr_version; std::string curr_policy; bool should_apply_policy; diff --git a/components/security_apps/orchestration/include/fog_authenticator.h b/components/security_apps/orchestration/include/fog_authenticator.h index 1bdc3f6..a80e593 100755 --- a/components/security_apps/orchestration/include/fog_authenticator.h +++ b/components/security_apps/orchestration/include/fog_authenticator.h @@ -142,6 +142,7 @@ protected: std::string base64Encode(const std::string &in) const; std::string buildBasicAuthHeader(const std::string &username, const std::string &pass) const; std::string buildOAuth2Header(const std::string &token) const; + std::string getUserEdition() const; // This apps which the orchestrations requires them from Fog. std::vector required_security_apps; diff --git a/components/security_apps/orchestration/include/get_status_rest.h b/components/security_apps/orchestration/include/get_status_rest.h index 655389f..d1b4a88 100755 --- a/components/security_apps/orchestration/include/get_status_rest.h +++ b/components/security_apps/orchestration/include/get_status_rest.h @@ -54,6 +54,7 @@ public: last_update = i_orch_status->getUpdateTime(); last_update_status = i_orch_status->getUpdateStatus(); policy_version = i_orch_status->getPolicyVersion(); + waap_model_version = i_orch_status->getWaapModelVersion(); last_policy_update = i_orch_status->getLastPolicyUpdate(); last_manifest_update = i_orch_status->getLastManifestUpdate(); last_settings_update = i_orch_status->getLastSettingsUpdate(); @@ -72,6 +73,7 @@ private: S2C_LABEL_PARAM(std::string, last_update, "Last update"); S2C_LABEL_PARAM(std::string, last_update_status, "Last update status"); S2C_LABEL_PARAM(std::string, policy_version, "Policy version"); + S2C_LABEL_PARAM(std::string, waap_model_version, "AI model version"); S2C_LABEL_PARAM(std::string, last_policy_update, "Last policy update"); S2C_LABEL_PARAM(std::string, last_manifest_update, "Last manifest update"); S2C_LABEL_PARAM(std::string, last_settings_update, "Last settings update"); diff --git a/components/security_apps/orchestration/include/mock/mock_orchestration_status.h b/components/security_apps/orchestration/include/mock/mock_orchestration_status.h index 740cfa5..a848973 100644 --- a/components/security_apps/orchestration/include/mock/mock_orchestration_status.h +++ b/components/security_apps/orchestration/include/mock/mock_orchestration_status.h @@ -45,6 +45,7 @@ public: MOCK_CONST_METHOD0(getUpdateTime, const std::string&()); MOCK_CONST_METHOD0(getLastManifestUpdate, const std::string&()); MOCK_CONST_METHOD0(getPolicyVersion, const std::string&()); + MOCK_CONST_METHOD0(getWaapModelVersion, const std::string&()); MOCK_CONST_METHOD0(getLastPolicyUpdate, const std::string&()); MOCK_CONST_METHOD0(getLastSettingsUpdate, const std::string&()); MOCK_CONST_METHOD0(getUpgradeMode, const std::string&()); diff --git a/components/security_apps/orchestration/local_policy_mgmt_gen/include/namespace_data.h b/components/security_apps/orchestration/include/namespace_data.h similarity index 100% rename from components/security_apps/orchestration/local_policy_mgmt_gen/include/namespace_data.h rename to components/security_apps/orchestration/include/namespace_data.h diff --git a/components/security_apps/orchestration/local_policy_mgmt_gen/CMakeLists.txt b/components/security_apps/orchestration/local_policy_mgmt_gen/CMakeLists.txt deleted file mode 100644 index 08b99a7..0000000 --- a/components/security_apps/orchestration/local_policy_mgmt_gen/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -include_directories(include) - -add_library(local_policy_mgmt_gen appsec_practice_section.cc exceptions_section.cc ingress_data.cc local_policy_mgmt_gen.cc policy_maker_utils.cc rules_config_section.cc settings_section.cc snort_section.cc triggers_section.cc trusted_sources_section.cc k8s_policy_utils.cc namespace_data.cc new_appsec_linux_policy.cc new_appsec_policy_crd_parser.cc new_custom_response.cc new_exceptions.cc new_log_trigger.cc new_practice.cc new_trusted_sources.cc access_control_practice.cc) diff --git a/components/security_apps/orchestration/local_policy_mgmt_gen/include/k8s_policy_common.h b/components/security_apps/orchestration/local_policy_mgmt_gen/include/k8s_policy_common.h deleted file mode 100644 index 29d8705..0000000 --- a/components/security_apps/orchestration/local_policy_mgmt_gen/include/k8s_policy_common.h +++ /dev/null @@ -1,106 +0,0 @@ -// 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 __K8S_POLICY_COMMON_H__ -#define __K8S_POLICY_COMMON_H__ - -#include -#include -#include -#include - -#include "config.h" -#include "debug.h" -#include "rest.h" - -USE_DEBUG_FLAG(D_LOCAL_POLICY); -// LCOV_EXCL_START Reason: no test exist -enum class PracticeType { WebApplication, WebAPI }; -enum class TriggerType { Log, WebUserResponse }; -enum class MatchType { Condition, Operator }; - -static const std::unordered_map string_to_match_type = { - { "condition", MatchType::Condition }, - { "operator", MatchType::Operator } -}; - -static const std::unordered_map string_to_practice_type = { - { "WebApplication", PracticeType::WebApplication }, - { "WebAPI", PracticeType::WebAPI } -}; - -static const std::unordered_map string_to_trigger_type = { - { "log", TriggerType::Log }, - { "WebUserResponse", TriggerType::WebUserResponse } -}; - -static const std::unordered_map key_to_practices_val = { - { "prevent-learn", "Prevent"}, - { "detect-learn", "Detect"}, - { "prevent", "Prevent"}, - { "detect", "Detect"}, - { "inactive", "Inactive"} -}; - -template -void -parseAppsecJSONKey( - const std::string &key_name, - T &value, - cereal::JSONInputArchive &archive_in, - const T &default_value = T()) -{ - try { - archive_in(cereal::make_nvp(key_name, value)); - } catch (const cereal::Exception &e) { - archive_in.setNextName(nullptr); - value = default_value; - dbgDebug(D_LOCAL_POLICY) - << "Could not parse the required key. Key: " - << key_name - << ", Error: " - << e.what(); - } -} - -template -class AppsecSpecParser : public ClientRest -{ -public: - AppsecSpecParser() = default; - AppsecSpecParser(const T &_spec) : spec(_spec) {} - - bool - loadJson(const std::string &json) - { - std::string modified_json = json; - modified_json.pop_back(); - std::stringstream ss; - ss.str(modified_json); - try { - cereal::JSONInputArchive in_ar(ss); - in_ar(cereal::make_nvp("spec", spec)); - } catch (cereal::Exception &e) { - dbgError(D_LOCAL_POLICY) << "Failed to load spec JSON. Error: " << e.what(); - return false; - } - return true; - } - - const T & getSpec() const { return spec; } - -private: - T spec; -}; -// LCOV_EXCL_STOP -#endif // __K8S_POLICY_COMMON_H__ diff --git a/components/security_apps/orchestration/manifest_controller/manifest_controller.cc b/components/security_apps/orchestration/manifest_controller/manifest_controller.cc index 7d5ae76..d2336c8 100755 --- a/components/security_apps/orchestration/manifest_controller/manifest_controller.cc +++ b/components/security_apps/orchestration/manifest_controller/manifest_controller.cc @@ -76,6 +76,7 @@ public: private: bool changeManifestFile(const string &new_manifest_file); + bool updateIgnoreListForNSaaS(); bool handlePackage( @@ -155,12 +156,36 @@ ManifestController::Impl::init() } } +bool +ManifestController::Impl::updateIgnoreListForNSaaS() +{ + if (!getProfileAgentSettingWithDefault(false, "accessControl.isAwsNSaaS")) return false; + + auto ignore_packages_path = getConfigurationWithDefault( + getFilesystemPathConfig() + "/conf/ignore-packages.txt", + "orchestration", + "Ignore packages list file path" + ); + ofstream ignore_file(ignore_packages_path); + if (!ignore_file.is_open()) { + dbgWarning(D_ORCHESTRATOR) << "Unable to open file " << ignore_packages_path << " for writing"; + return false; + } + + ignore_file << "all"; + ignore_file.close(); + dbgInfo(D_ORCHESTRATOR) << "Updated " << ignore_packages_path << " to ignore all packages"; + + return true; +} + bool ManifestController::Impl::updateManifest(const string &new_manifest_file) { auto i_env = Singleton::Consume::by(); auto span_scope = i_env->startNewSpanScope(Span::ContextType::CHILD_OF); auto orchestration_tools = Singleton::Consume::by(); + static bool ignore_packages_update = false; if (isIgnoreFile(new_manifest_file)) { if (!orchestration_tools->copyFile(new_manifest_file, manifest_file_path)) { @@ -173,9 +198,12 @@ ManifestController::Impl::updateManifest(const string &new_manifest_file) dbgDebug(D_ORCHESTRATOR) << "Starting to update manifest file"; auto ignored_settings_packages = getProfileAgentSetting("orchestration.IgnoredPackagesList"); set packages_to_ignore = ignore_packages; - if (ignored_settings_packages.ok()) packages_to_ignore = *(*ignored_settings_packages); + if (ignored_settings_packages.ok()) { + packages_to_ignore = *(*ignored_settings_packages); + ignore_packages_update = false; + } - if (packages_to_ignore.count("all") > 0) { + if (ignore_packages_update || packages_to_ignore.count("all") > 0) { dbgTrace(D_ORCHESTRATOR) << "Nothing to update (\"ignore all\" turned on)"; if (!orchestration_tools->copyFile(new_manifest_file, manifest_file_path)) { @@ -315,6 +343,8 @@ ManifestController::Impl::updateManifest(const string &new_manifest_file) if (all_installed && (any_installed || no_change) && no_corrupted_package) { manifest_file_update = changeManifestFile(new_manifest_file); + // In NSaaS - set ignore packages to any + ignore_packages_update = updateIgnoreListForNSaaS(); } else if (any_installed) { manifest_file_update = orchestration_tools->packagesToJsonFile(current_packages, manifest_file_path); } diff --git a/components/security_apps/orchestration/modules/modules_ut/orchestration_status_ut.cc b/components/security_apps/orchestration/modules/modules_ut/orchestration_status_ut.cc index 6c902ed..07eb0b9 100755 --- a/components/security_apps/orchestration/modules/modules_ut/orchestration_status_ut.cc +++ b/components/security_apps/orchestration/modules/modules_ut/orchestration_status_ut.cc @@ -11,6 +11,7 @@ #include "mock/mock_time_get.h" #include "mock/mock_orchestration_tools.h" #include "mock/mock_agent_details.h" +#include "mock/mock_details_resolver.h" #include "mock/mock_mainloop.h" #include "mock/mock_rest_api.h" @@ -38,9 +39,17 @@ public: .WillOnce(DoAll(SaveArg<2>(&routine), Return(1)) ); EXPECT_CALL(mock_tools, readFile(file_path)).WillOnce(Return(start_file_content)); + prepareResolvedDetails(); orchestration_status.init(); } + void + prepareResolvedDetails() + { + map resolved_details({{"AppSecModelVersion", waap_model}}); + EXPECT_CALL(mock_details_resolver, getResolvedDetails()).WillRepeatedly(Return(resolved_details)); + } + string orchestrationStatusFileToString() { @@ -82,7 +91,8 @@ public: const string ®istration_details_architecture = "", const string &agent_id = "None", const string &profile_id = "None", - const string &tenant_id = "None" + const string &tenant_id = "None", + const string &waap_model_version = "Advanced model" ) { return "{\n" @@ -91,6 +101,7 @@ public: " \"Last update\": \"" + last_update + "\",\n" " \"Last manifest update\": \"" + last_manifest_update + "\",\n" " \"Policy version\": \"" + policy_version + "\",\n" + " \"AI model version\": \"" + waap_model_version + "\",\n" " \"Last policy update\": \"" + last_policy_update + "\",\n" " \"Last settings update\": \"" + last_settings_update + "\",\n" " \"Upgrade mode\": \"" + upgrade_mode + "\",\n" @@ -118,12 +129,14 @@ public: ostringstream capture_debug; StrictMock mock_tools; StrictMock mock_agent_details; + StrictMock mock_details_resolver; OrchestrationStatus orchestration_status; I_OrchestrationStatus * i_orchestration_status = Singleton::Consume::from(orchestration_status); string file_path; Maybe start_file_content = genError("No file"); I_MainLoop::Routine routine; + string waap_model = "Advanced model"; }; TEST_F(OrchestrationStatusTest, doNothing) @@ -147,6 +160,7 @@ TEST_F(OrchestrationStatusTest, recoverFields) TEST_F(OrchestrationStatusTest, loadFromFile) { + prepareResolvedDetails(); Maybe status = genError("No file");; CPTestTempfile status_file; file_path = status_file.fname; @@ -214,12 +228,14 @@ TEST_F(OrchestrationStatusTest, recoveryFields) const string agent_id = "AgentId"; const string profile_id = "ProfileId"; const string tenant_id = "TenantId"; + auto fog_addr = Maybe(string("FogDomain")); EXPECT_CALL(mock_agent_details, getAgentId()).WillOnce(Return(agent_id)); EXPECT_CALL(mock_agent_details, getProfileId()).WillOnce(Return(profile_id)); EXPECT_CALL(mock_agent_details, getTenantId()).WillOnce(Return(tenant_id)); EXPECT_CALL(mock_agent_details, getFogDomain()).WillOnce(Return(fog_addr)); + i_orchestration_status->writeStatusToFile(); EXPECT_THAT(capture_debug.str(), HasSubstr("Repairing status fields")); @@ -227,6 +243,7 @@ TEST_F(OrchestrationStatusTest, recoveryFields) EXPECT_EQ(i_orchestration_status->getProfileId(), profile_id); EXPECT_EQ(i_orchestration_status->getTenantId(), tenant_id); EXPECT_EQ(i_orchestration_status->getFogAddress(), fog_addr.unpack()); + EXPECT_EQ(i_orchestration_status->getWaapModelVersion(), waap_model); } TEST_F(OrchestrationStatusTest, updateAllLastUpdatesTypes) @@ -419,6 +436,7 @@ TEST_F(OrchestrationStatusTest, setAllFields) " \"Last update\": \"current time\",\n" " \"Last manifest update\": \"current time\",\n" " \"Policy version\": \"12\",\n" + " \"AI model version\": \"Advanced model\",\n" " \"Last policy update\": \"current time\",\n" " \"Last settings update\": \"current time\",\n" " \"Upgrade mode\": \"Test Mode\",\n" diff --git a/components/security_apps/orchestration/modules/orchestration_status.cc b/components/security_apps/orchestration/modules/orchestration_status.cc index 183ca05..4a45a20 100755 --- a/components/security_apps/orchestration/modules/orchestration_status.cc +++ b/components/security_apps/orchestration/modules/orchestration_status.cc @@ -108,6 +108,7 @@ public: last_update_attempt = from.last_update_attempt; last_manifest_update = from.last_manifest_update; policy_version = from.policy_version; + waap_model_version = from.waap_model_version; last_policy_update = from.last_policy_update; last_settings_update = from.last_settings_update; upgrade_mode = from.upgrade_mode; @@ -128,6 +129,7 @@ public: const string & getUpdateTime() const { return last_update_time; } const string & getLastManifestUpdate() const { return last_manifest_update; } const string & getPolicyVersion() const { return policy_version; } + const string & getWaapModelVersion() const { return waap_model_version; } const string & getLastPolicyUpdate() const { return last_policy_update; } const string & getLastSettingsUpdate() const { return last_settings_update; } const string & getUpgradeMode() const { return upgrade_mode; } @@ -142,6 +144,16 @@ public: const map & getServicePolicies() const { return service_policies; } const map & getServiceSettings() const { return service_settings; } + void updateWaapModelVersion() { + map details_resolver = + Singleton::Consume::by()->getResolvedDetails(); + if (details_resolver.find("AppSecModelVersion") != details_resolver.end()) { + waap_model_version = details_resolver["AppSecModelVersion"]; + } else { + waap_model_version = "None"; + } + } + void insertServicePolicy(const string &key, const string &value) { @@ -267,12 +279,13 @@ public: last_manifest_update = "None"; last_policy_update = "None"; last_settings_update = "None"; + waap_model_version = "None"; fog_address = "None"; agent_id = "None"; profile_id = "None"; tenant_id = "None"; registration_status = "None"; - manifest_status = "None"; + manifest_status = getenv("CLOUDGUARD_APPSEC_STANDALONE") ? "Succeeded" : "None"; upgrade_mode = "None"; } @@ -292,6 +305,7 @@ public: } else { fog_address = "None"; } + updateWaapModelVersion(); } } @@ -304,6 +318,7 @@ public: archive(cereal::make_nvp("Last update", last_update_time)); archive(cereal::make_nvp("Last manifest update", last_manifest_update)); archive(cereal::make_nvp("Policy version", policy_version)); + archive(cereal::make_nvp("AI model version", waap_model_version)); archive(cereal::make_nvp("Last policy update", last_policy_update)); archive(cereal::make_nvp("Last settings update", last_settings_update)); archive(cereal::make_nvp("Upgrade mode", upgrade_mode)); @@ -331,6 +346,7 @@ public: archive.setNextName(nullptr); } + archive(cereal::make_nvp("AI model version", waap_model_version)); archive(cereal::make_nvp("Last policy update", last_policy_update)); archive(cereal::make_nvp("Last settings update", last_settings_update)); @@ -368,6 +384,7 @@ private: string last_update_attempt; string last_manifest_update; string policy_version; + string waap_model_version; string last_policy_update; string last_settings_update; string upgrade_mode; @@ -387,13 +404,14 @@ class OrchestrationStatus::Impl : Singleton::Provide::Fro { public: void - writeStatusToFile() + writeStatusToFile() override { auto orchestration_status_path = getConfigurationWithDefault( filesystem_prefix + "/conf/orchestration_status.json", "orchestration", "Orchestration status path" ); + status.updateWaapModelVersion(); auto write_result = orchestration_tools->objectToJsonFile(status, orchestration_status_path); if (!write_result) { @@ -497,6 +515,7 @@ private: const string & getUpdateTime() const override { return status.getUpdateTime(); } const string & getLastManifestUpdate() const override { return status.getLastManifestUpdate(); } const string & getPolicyVersion() const override { return status.getPolicyVersion(); } + const string & getWaapModelVersion() const override { return status.getWaapModelVersion(); } const string & getLastPolicyUpdate() const override { return status.getLastPolicyUpdate(); } const string & getLastSettingsUpdate() const override { return status.getLastSettingsUpdate(); } const string & getUpgradeMode() const override { return status.getUpgradeMode(); } diff --git a/components/security_apps/orchestration/orchestration_comp.cc b/components/security_apps/orchestration/orchestration_comp.cc index 771335b..710103c 100755 --- a/components/security_apps/orchestration/orchestration_comp.cc +++ b/components/security_apps/orchestration/orchestration_comp.cc @@ -189,6 +189,10 @@ public: "Orchestration runner", true ); + + auto orchestration_tools = Singleton::Consume::by(); + orchestration_tools->getClusterId(); + hybrid_mode_metric.init( "Watchdog Metrics", ReportIS::AudienceTeam::AGENT_CORE, @@ -198,7 +202,6 @@ public: ReportIS::Audience::INTERNAL ); hybrid_mode_metric.registerListener(); - auto orchestration_tools = Singleton::Consume::by(); orchestration_tools->loadTenantsFromDir( getConfigurationWithDefault(getFilesystemPathConfig() + "/conf/", "orchestration", "Conf dir") ); @@ -1485,6 +1488,9 @@ private: if (i_details_resolver->compareCheckpointVersion(8100, greater_equal())) { agent_data_report << AgentReportFieldWithLabel("isCheckpointVersionGER81", "true"); } + if (i_details_resolver->compareCheckpointVersion(8200, greater_equal())) { + agent_data_report << AgentReportFieldWithLabel("isCheckpointVersionGER82", "true"); + } #endif // gaia || smb if (agent_data_report == curr_agent_data_report) { diff --git a/components/security_apps/orchestration/orchestration_tools/CMakeLists.txt b/components/security_apps/orchestration/orchestration_tools/CMakeLists.txt index bed840f..08211d2 100755 --- a/components/security_apps/orchestration/orchestration_tools/CMakeLists.txt +++ b/components/security_apps/orchestration/orchestration_tools/CMakeLists.txt @@ -1,5 +1,5 @@ ADD_DEFINITIONS(-Wno-deprecated-declarations) -add_library(orchestration_tools orchestration_tools.cc) +add_library(orchestration_tools orchestration_tools.cc namespace_data.cc) #add_subdirectory(orchestration_tools_ut) diff --git a/components/security_apps/orchestration/local_policy_mgmt_gen/namespace_data.cc b/components/security_apps/orchestration/orchestration_tools/namespace_data.cc similarity index 69% rename from components/security_apps/orchestration/local_policy_mgmt_gen/namespace_data.cc rename to components/security_apps/orchestration/orchestration_tools/namespace_data.cc index c4b65b3..9479ef3 100644 --- a/components/security_apps/orchestration/local_policy_mgmt_gen/namespace_data.cc +++ b/components/security_apps/orchestration/orchestration_tools/namespace_data.cc @@ -12,11 +12,31 @@ // limitations under the License. #include "namespace_data.h" -#include "local_policy_common.h" using namespace std; -USE_DEBUG_FLAG(D_LOCAL_POLICY); +USE_DEBUG_FLAG(D_ORCHESTRATOR); + +template +void +parseNameSpaceJSONKey( + const string &key_name, + T &value, + cereal::JSONInputArchive &archive_in, + const T &default_value = T()) +{ + try { + archive_in(cereal::make_nvp(key_name, value)); + } catch (const cereal::Exception &e) { + archive_in.setNextName(nullptr); + value = default_value; + dbgDebug(D_ORCHESTRATOR) + << "Could not parse the required key. Key: " + << key_name + << ", Error: " + << e.what(); + } +} class NamespaceMetadata { @@ -24,9 +44,9 @@ public: void load(cereal::JSONInputArchive &archive_in) { - dbgFlow(D_LOCAL_POLICY); - parseAppsecJSONKey("name", name, archive_in); - parseAppsecJSONKey("uid", uid, archive_in); + dbgFlow(D_ORCHESTRATOR); + parseNameSpaceJSONKey("name", name, archive_in); + parseNameSpaceJSONKey("uid", uid, archive_in); } const string & @@ -52,7 +72,7 @@ public: void load(cereal::JSONInputArchive &archive_in) { - parseAppsecJSONKey("metadata", metadata, archive_in); + parseNameSpaceJSONKey("metadata", metadata, archive_in); } const NamespaceMetadata & @@ -68,7 +88,7 @@ private: bool NamespaceData::loadJson(const string &json) { - dbgFlow(D_LOCAL_POLICY); + dbgFlow(D_ORCHESTRATOR); string modified_json = json; modified_json.pop_back(); stringstream in; @@ -81,7 +101,7 @@ NamespaceData::loadJson(const string &json) ns_name_to_uid[single_ns_data.getMetadata().getName()] = single_ns_data.getMetadata().getUID(); } } catch (cereal::Exception &e) { - dbgWarning(D_LOCAL_POLICY) << "Failed to load namespace data JSON. Error: " << e.what(); + dbgWarning(D_ORCHESTRATOR) << "Failed to load namespace data JSON. Error: " << e.what(); return false; } return true; diff --git a/components/security_apps/orchestration/orchestration_tools/orchestration_tools.cc b/components/security_apps/orchestration/orchestration_tools/orchestration_tools.cc index ff4d60a..952dac6 100755 --- a/components/security_apps/orchestration/orchestration_tools/orchestration_tools.cc +++ b/components/security_apps/orchestration/orchestration_tools/orchestration_tools.cc @@ -19,6 +19,7 @@ #include "cereal/types/vector.hpp" #include "cereal/types/set.hpp" #include "agent_core_utilities.h" +#include "namespace_data.h" #include #include @@ -47,11 +48,13 @@ public: const string &tenant_id, const string &profile_id) const override; + shared_ptr fileStreamWrapper(const std::string &path) const override; Maybe readFile(const string &path) const override; - bool writeFile(const string &text, const string &path) const override; + bool writeFile(const string &text, const string &path, bool append_mode = false) const override; bool removeFile(const string &path) const override; bool copyFile(const string &src_path, const string &dst_path) const override; bool doesFileExist(const string &file_path) const override; + void getClusterId() const override; void fillKeyInJson(const string &filename, const string &_key, const string &_val) const override; bool createDirectory(const string &directory_path) const override; bool doesDirectoryExist(const string &dir_path) const override; @@ -127,6 +130,98 @@ OrchestrationTools::Impl::fillKeyInJson(const string &filename, const string &_k } // LCOV_EXCL_STOP +bool +isPlaygroundEnv() +{ + const char *env_string = getenv("PLAYGROUND"); + + if (env_string == nullptr) return false; + string env_value = env_string; + transform(env_value.begin(), env_value.end(), env_value.begin(), ::tolower); + + return env_value == "true"; +} + +Maybe +getNamespaceDataFromCluster(const string &path) +{ + NamespaceData name_space; + string token = Singleton::Consume::by()->getToken(); + Flags conn_flags; + conn_flags.setFlag(MessageConnConfig::SECURE_CONN); + conn_flags.setFlag(MessageConnConfig::IGNORE_SSL_VALIDATION); + auto messaging = Singleton::Consume::by(); + bool res = messaging->sendObject( + name_space, + I_Messaging::Method::GET, + "kubernetes.default.svc", + 443, + conn_flags, + path, + "Authorization: Bearer " + token + "\nConnection: close" + ); + + if (res) return name_space; + + return genError(string("Was not able to get object form k8s cluser in path: " + path)); +} + +bool +doesClusterIdExists() +{ + string playground_uid = isPlaygroundEnv() ? "playground-" : ""; + + dbgTrace(D_ORCHESTRATOR) << "Getting cluster UID"; + + auto maybe_namespaces_data = getNamespaceDataFromCluster("/api/v1/namespaces/"); + + if (!maybe_namespaces_data.ok()) { + dbgWarning(D_ORCHESTRATOR) + << "Failed to retrieve K8S namespace data. Error: " + << maybe_namespaces_data.getErr(); + return false; + } + + NamespaceData namespaces_data = maybe_namespaces_data.unpack(); + + Maybe maybe_ns_uid = namespaces_data.getNamespaceUidByName("kube-system"); + if (!maybe_ns_uid.ok()) { + dbgWarning(D_ORCHESTRATOR) << maybe_ns_uid.getErr(); + return false; + } + string uid = playground_uid + maybe_ns_uid.unpack(); + dbgTrace(D_ORCHESTRATOR) << "Found k8s cluster UID: " << uid; + I_Environment *env = Singleton::Consume::by(); + env->getConfigurationContext().registerValue( + "k8sClusterId", + uid, + EnvKeyAttr::LogSection::SOURCE + ); + I_AgentDetails *i_agent_details = Singleton::Consume::by(); + i_agent_details->setClusterId(uid); + return true; +} + +void +OrchestrationTools::Impl::getClusterId() const +{ + auto env_type = Singleton::Consume::by()->getEnvType(); + + if (env_type == EnvType::K8S) { + Singleton::Consume::by()->addOneTimeRoutine( + I_MainLoop::RoutineType::Offline, + [this] () + { + while(!doesClusterIdExists()) { + Singleton::Consume::by()->yield(chrono::seconds(1)); + } + return; + }, + "Get k8s cluster ID" + ); + } +} + bool OrchestrationTools::Impl::doesFileExist(const string &file_path) const { @@ -140,7 +235,7 @@ OrchestrationTools::Impl::doesDirectoryExist(const string &dir_path) const } bool -OrchestrationTools::Impl::writeFile(const string &text, const string &path) const +OrchestrationTools::Impl::writeFile(const string &text, const string &path, bool append_mode) const { dbgDebug(D_ORCHESTRATOR) << "Writing file: text = " << text << ", path = " << path; if (path.find('/') != string::npos) { @@ -151,8 +246,15 @@ OrchestrationTools::Impl::writeFile(const string &text, const string &path) cons return false; } } + + ofstream fout; + + if (append_mode) { + fout.open(path, std::ios::app); + } else { + fout.open(path); + } try { - ofstream fout(path); fout << text; return true; } catch (const ofstream::failure &e) { @@ -186,6 +288,12 @@ OrchestrationTools::Impl::isNonEmptyFile(const string &path) const return false; } +shared_ptr +OrchestrationTools::Impl::fileStreamWrapper(const std::string &path) const +{ + return make_shared(path); +} + Maybe OrchestrationTools::Impl::readFile(const string &path) const { diff --git a/components/security_apps/orchestration/orchestration_tools/orchestration_tools_ut/orchestration_tools_ut.cc b/components/security_apps/orchestration/orchestration_tools/orchestration_tools_ut/orchestration_tools_ut.cc index c229aca..02581ed 100755 --- a/components/security_apps/orchestration/orchestration_tools/orchestration_tools_ut/orchestration_tools_ut.cc +++ b/components/security_apps/orchestration/orchestration_tools/orchestration_tools_ut/orchestration_tools_ut.cc @@ -1,8 +1,13 @@ #include "orchestration_tools.h" #include "cptest.h" +#include "config_component.h" #include "mock/mock_tenant_manager.h" #include "mock/mock_shell_cmd.h" +#include "mock/mock_messaging.h" +#include "mock/mock_env_details.h" +#include "mock/mock_agent_details.h" +#include "mock/mock_mainloop.h" using namespace std; using namespace testing; @@ -14,6 +19,17 @@ public: { } + string + getResource(const string &path) + { + string resource_path = cptestFnameInSrcDir(path); + ifstream resource_file(resource_path); + EXPECT_TRUE(resource_file.is_open()); + stringstream resource_file_content; + resource_file_content << resource_file.rdbuf(); + return resource_file_content.str(); + } + void cleanSpaces(string &str) { @@ -47,27 +63,74 @@ public: OrchestrationTools orchestration_tools; I_OrchestrationTools *i_orchestration_tools = Singleton::Consume::from(orchestration_tools); - StrictMock mock_tenant_manager; + NiceMock mock_messaging; + NiceMock mock_agent_details; + NiceMock mock_mainloop; StrictMock mock_shell_cmd; + StrictMock mock_env_details; + StrictMock mock_tenant_manager; + ::Environment env; + }; TEST_F(OrchestrationToolsTest, doNothing) { } +TEST_F(OrchestrationToolsTest, getClusterId) +{ + EXPECT_CALL(mock_env_details, getToken()).WillOnce(Return("123")); + EXPECT_CALL(mock_env_details, getEnvType()).WillOnce(Return(EnvType::K8S)); + I_MainLoop::Routine routine; + EXPECT_CALL( + mock_mainloop, + addOneTimeRoutine(I_MainLoop::RoutineType::Offline, _, "Get k8s cluster ID", _) + ).WillOnce(DoAll(SaveArg<1>(&routine), Return(1))); + + string namespaces = getResource("k8s_namespaces.json"); + EXPECT_CALL( + mock_messaging, + sendMessage( + true, + "", + I_Messaging::Method::GET, + "kubernetes.default.svc", + 443, + _, + "/api/v1/namespaces/", + "Authorization: Bearer 123\nConnection: close", + _, + _ + ) + ).WillRepeatedly(Return(Maybe(namespaces))); + i_orchestration_tools->getClusterId(); + routine(); +} + TEST_F(OrchestrationToolsTest, writeReadTextToFile) { - EXPECT_TRUE(i_orchestration_tools->writeFile(manifest_text, manifest_file)); + EXPECT_TRUE(i_orchestration_tools->writeFile(manifest_text, manifest_file, false)); EXPECT_TRUE(i_orchestration_tools->doesFileExist(manifest_file)); EXPECT_TRUE(i_orchestration_tools->isNonEmptyFile(manifest_file)); + EXPECT_TRUE(i_orchestration_tools->fileStreamWrapper(manifest_file)->is_open()); EXPECT_EQ(manifest_text, i_orchestration_tools->readFile(manifest_file).unpack()); EXPECT_FALSE(i_orchestration_tools->isNonEmptyFile("no_such_file")); } +TEST_F(OrchestrationToolsTest, writeAndAppendToFile) +{ + EXPECT_TRUE(i_orchestration_tools->writeFile("blabla", "in_test.json", false)); + EXPECT_TRUE(i_orchestration_tools->doesFileExist("in_test.json")); + EXPECT_TRUE(i_orchestration_tools->isNonEmptyFile("in_test.json")); + EXPECT_TRUE(i_orchestration_tools->writeFile(" Appending Text", "in_test.json", true)); + + EXPECT_EQ("blabla Appending Text", i_orchestration_tools->readFile("in_test.json").unpack());; +} + TEST_F(OrchestrationToolsTest, loadPackagesFromJsonTest) { - EXPECT_TRUE(i_orchestration_tools->writeFile("blabla", "in_test.json")); + EXPECT_TRUE(i_orchestration_tools->writeFile("blabla", "in_test.json", false)); string file_name = "in_test.json"; Maybe> packages = i_orchestration_tools->loadPackagesFromJson(file_name); EXPECT_FALSE(packages.ok()); @@ -83,7 +146,7 @@ TEST_F(OrchestrationToolsTest, loadPackagesFromJsonTest) TEST_F(OrchestrationToolsTest, copyFile) { - EXPECT_TRUE(i_orchestration_tools->writeFile("blabla", "in_test.json")); + EXPECT_TRUE(i_orchestration_tools->writeFile("blabla", "in_test.json", false)); EXPECT_TRUE(i_orchestration_tools->copyFile("in_test.json", "cpy_test.json")); EXPECT_EQ("blabla", i_orchestration_tools->readFile("cpy_test.json").unpack()); EXPECT_FALSE(i_orchestration_tools->copyFile("NOT_EXISTS_FILE", "cpy2_test.json")); @@ -199,7 +262,7 @@ TEST_F(OrchestrationToolsTest, jsonFileToPackages) " }" " ]" "}"; - i_orchestration_tools->writeFile(string_stream.str(), "packages_tmp.json"); + i_orchestration_tools->writeFile(string_stream.str(), "packages_tmp.json", false); Maybe> packages = i_orchestration_tools->loadPackagesFromJson("packages_tmp.json"); EXPECT_TRUE(packages.ok()); EXPECT_TRUE(packages.unpack().find("nano-agent") != packages.unpack().end()); @@ -222,7 +285,7 @@ TEST_F(OrchestrationToolsTest, packagesToJsonFile) " }" " ]" "}"; - i_orchestration_tools->writeFile(string_stream.str(), "packages.json"); + i_orchestration_tools->writeFile(string_stream.str(), "packages.json", false); Maybe> packages = i_orchestration_tools->loadPackagesFromJson("packages.json"); EXPECT_TRUE(packages.ok()); EXPECT_TRUE(i_orchestration_tools->packagesToJsonFile(packages.unpack(), "packages.json")); @@ -277,8 +340,8 @@ TEST_F(OrchestrationToolsTest, deleteVirtualTenantFiles) EXPECT_TRUE(i_orchestration_tools->createDirectory(policy_folder_path)); string settings_file_path = conf_path + "/tenant_3fdbdd33_profile_c4c498d8_settings.json"; - i_orchestration_tools->writeFile(string_stream.str(), settings_file_path); - i_orchestration_tools->writeFile(string_stream.str(), policy_file_path); + i_orchestration_tools->writeFile(string_stream.str(), settings_file_path, false); + i_orchestration_tools->writeFile(string_stream.str(), policy_file_path, false); EXPECT_TRUE(i_orchestration_tools->doesFileExist(settings_file_path)); EXPECT_TRUE(i_orchestration_tools->doesFileExist(policy_file_path)); @@ -301,16 +364,16 @@ TEST_F(OrchestrationToolsTest, loadTenants) EXPECT_TRUE(i_orchestration_tools->createDirectory(policy_folder_path2)); string settings_file_path1 = conf_path + "/tenant_3fdbdd33_profile_c4c498d8_settings.json"; - i_orchestration_tools->writeFile(string_stream.str(), settings_file_path1); + i_orchestration_tools->writeFile(string_stream.str(), settings_file_path1, false); string settings_file_path2 = conf_path + "/tenant_123456_profile_654321_settings.json"; - i_orchestration_tools->writeFile(string_stream.str(), settings_file_path2); + i_orchestration_tools->writeFile(string_stream.str(), settings_file_path2, false); string policy_file_path1 = policy_folder_path1 + "/policy.json"; - i_orchestration_tools->writeFile(string_stream.str(), policy_file_path1); + i_orchestration_tools->writeFile(string_stream.str(), policy_file_path1, false); string policy_file_path2 = policy_folder_path2 + "/policy.json"; - i_orchestration_tools->writeFile(string_stream.str(), policy_file_path2); + i_orchestration_tools->writeFile(string_stream.str(), policy_file_path2, false); EXPECT_TRUE(i_orchestration_tools->doesFileExist(settings_file_path1)); EXPECT_TRUE(i_orchestration_tools->doesFileExist(settings_file_path2)); diff --git a/components/security_apps/orchestration/orchestration_ut/orchestration_multitenant_ut.cc b/components/security_apps/orchestration/orchestration_ut/orchestration_multitenant_ut.cc index b118ddc..3d9c802 100644 --- a/components/security_apps/orchestration/orchestration_ut/orchestration_multitenant_ut.cc +++ b/components/security_apps/orchestration/orchestration_ut/orchestration_multitenant_ut.cc @@ -62,6 +62,8 @@ public: addOneTimeRoutine(I_MainLoop::RoutineType::RealTime, _, "Orchestration runner", true) ).WillOnce(DoAll(SaveArg<1>(&routine), Return(1))); + EXPECT_CALL(mock_orchestration_tools, getClusterId()); + EXPECT_CALL(mock_shell_cmd, getExecOutput("openssl version -d | cut -d\" \" -f2 | cut -d\"\\\"\" -f2", _, _)) .WillOnce(Return(string("OpenSSL certificates Directory"))); @@ -91,11 +93,11 @@ public: Maybe err = genError("No file exist"); EXPECT_CALL(mock_orchestration_tools, readFile("/etc/cp/conf/user-cred.json")).WillOnce(Return(err)); - EXPECT_CALL(mock_orchestration_tools, writeFile("This is fake", "/etc/cp/data/data1.a")).WillOnce( + EXPECT_CALL(mock_orchestration_tools, writeFile("This is fake", "/etc/cp/data/data1.a", false)).WillOnce( Return(true)); - EXPECT_CALL(mock_orchestration_tools, writeFile("0000 is fake", "/etc/cp/data/data4.a")).WillOnce( + EXPECT_CALL(mock_orchestration_tools, writeFile("0000 is fake", "/etc/cp/data/data4.a", false)).WillOnce( Return(true)); - EXPECT_CALL(mock_orchestration_tools, writeFile("This is 3333", "/etc/cp/data/data6.a")).WillOnce( + EXPECT_CALL(mock_orchestration_tools, writeFile("This is 3333", "/etc/cp/data/data6.a", false)).WillOnce( Return(true)); } diff --git a/components/security_apps/orchestration/orchestration_ut/orchestration_ut.cc b/components/security_apps/orchestration/orchestration_ut/orchestration_ut.cc index d362fd6..88de151 100755 --- a/components/security_apps/orchestration/orchestration_ut/orchestration_ut.cc +++ b/components/security_apps/orchestration/orchestration_ut/orchestration_ut.cc @@ -54,6 +54,8 @@ public: addOneTimeRoutine(I_MainLoop::RoutineType::RealTime, _, "Orchestration runner", true) ).WillOnce(DoAll(SaveArg<1>(&routine), Return(1))); + EXPECT_CALL(mock_orchestration_tools, getClusterId()); + EXPECT_CALL( mock_shell_cmd, getExecOutput("openssl version -d | cut -d\" \" -f2 | cut -d\"\\\"\" -f2", _, _) @@ -118,11 +120,11 @@ public: Maybe err = genError("No file exist"); EXPECT_CALL(mock_orchestration_tools, readFile("/etc/cp/conf/user-cred.json")).WillOnce(Return(err)); - EXPECT_CALL(mock_orchestration_tools, writeFile("This is fake", "/etc/cp/data/data1.a")).WillOnce( + EXPECT_CALL(mock_orchestration_tools, writeFile("This is fake", "/etc/cp/data/data1.a", false)).WillOnce( Return(true)); - EXPECT_CALL(mock_orchestration_tools, writeFile("0000 is fake", "/etc/cp/data/data4.a")).WillOnce( + EXPECT_CALL(mock_orchestration_tools, writeFile("0000 is fake", "/etc/cp/data/data4.a", false)).WillOnce( Return(true)); - EXPECT_CALL(mock_orchestration_tools, writeFile("This is 3333", "/etc/cp/data/data6.a")).WillOnce( + EXPECT_CALL(mock_orchestration_tools, writeFile("This is 3333", "/etc/cp/data/data6.a", false)).WillOnce( Return(true)); } @@ -1333,26 +1335,6 @@ TEST_F(OrchestrationTest, manifestUpdate) } catch (const invalid_argument& e) {} } -TEST_F(OrchestrationTest, loadFromOrchestrationPolicy) -{ - EXPECT_CALL( - rest, - mockRestCall(RestAction::ADD, "proxy", _) - ).WillOnce(WithArg<2>(Invoke(this, &OrchestrationTest::restHandler))); - waitForRestCall(); - init(); -} - -TEST_F(OrchestrationTest, loadFromOrchestrationBackupPolicy) -{ - EXPECT_CALL( - rest, - mockRestCall(RestAction::ADD, "proxy", _) - ).WillOnce(WithArg<2>(Invoke(this, &OrchestrationTest::restHandler))); - waitForRestCall(); - init(); -} - TEST_F(OrchestrationTest, getBadPolicyUpdate) { EXPECT_CALL( @@ -1815,6 +1797,7 @@ TEST_F(OrchestrationTest, GetRestOrchStatus) " \"Last update\": \"" + test_str + "\",\n" " \"Last update status\": \"" + test_str + "\",\n" " \"Policy version\": \"" + test_str + "\",\n" + " \"AI model version\": \"" + test_str + "\",\n" " \"Last policy update\": \"" + test_str + "\",\n" " \"Last manifest update\": \"" + test_str + "\",\n" " \"Last settings update\": \"" + test_str + "\",\n" @@ -1841,6 +1824,7 @@ TEST_F(OrchestrationTest, GetRestOrchStatus) EXPECT_CALL(mock_status, getUpdateTime()).WillOnce(ReturnRef(test_str)); EXPECT_CALL(mock_status, getLastManifestUpdate()).WillOnce(ReturnRef(test_str)); EXPECT_CALL(mock_status, getPolicyVersion()).WillOnce(ReturnRef(test_str)); + EXPECT_CALL(mock_status, getWaapModelVersion()).WillOnce(ReturnRef(test_str)); EXPECT_CALL(mock_status, getLastPolicyUpdate()).WillOnce(ReturnRef(test_str)); EXPECT_CALL(mock_status, getLastSettingsUpdate()).WillOnce(ReturnRef(test_str)); EXPECT_CALL(mock_status, getUpgradeMode()).WillOnce(ReturnRef(test_str)); diff --git a/components/security_apps/orchestration/service_controller/service_controller_ut/service_controller_ut.cc b/components/security_apps/orchestration/service_controller/service_controller_ut/service_controller_ut.cc index 883a999..522ea39 100755 --- a/components/security_apps/orchestration/service_controller/service_controller_ut/service_controller_ut.cc +++ b/components/security_apps/orchestration/service_controller/service_controller_ut/service_controller_ut.cc @@ -246,7 +246,8 @@ TEST_F(ServiceControllerTest, UpdateConfiguration) EXPECT_CALL(mock_orchestration_tools, jsonObjectSplitter(new_configuration, _, _)) .WillOnce(Return(json_parser_return)); EXPECT_CALL(mock_orchestration_tools, doesFileExist(l4_firewall_policy_path)).WillOnce(Return(false)); - EXPECT_CALL(mock_orchestration_tools, writeFile(l4_firewall, l4_firewall_policy_path)).WillOnce(Return(true)); + EXPECT_CALL(mock_orchestration_tools, writeFile(l4_firewall, l4_firewall_policy_path, false)) + .WillOnce(Return(true)); EXPECT_CALL(mock_orchestration_status, setServiceConfiguration("l4_firewall", l4_firewall_policy_path, OrchestrationStatusConfigType::POLICY)); @@ -357,8 +358,9 @@ TEST_F(ServiceControllerTest, supportVersions) .WillOnce(Return(json_parser_return)); EXPECT_CALL(mock_orchestration_tools, doesFileExist(policy_versions_path)).WillOnce(Return(false)); EXPECT_CALL(mock_orchestration_tools, doesFileExist(l4_firewall_policy_path)).WillOnce(Return(false)); - EXPECT_CALL(mock_orchestration_tools, writeFile(l4_firewall, l4_firewall_policy_path)).WillOnce(Return(true)); - EXPECT_CALL(mock_orchestration_tools, writeFile(versions, policy_versions_path)).WillOnce(Return(true)); + EXPECT_CALL(mock_orchestration_tools, writeFile(l4_firewall, l4_firewall_policy_path, false)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_orchestration_tools, writeFile(versions, policy_versions_path, false)).WillOnce(Return(true)); EXPECT_CALL(mock_orchestration_status, setServiceConfiguration("versions", policy_versions_path, OrchestrationStatusConfigType::POLICY)); EXPECT_CALL(mock_orchestration_status, @@ -455,7 +457,8 @@ TEST_F(ServiceControllerTest, TimeOutUpdateConfiguration) EXPECT_CALL(mock_orchestration_tools, jsonObjectSplitter(new_configuration, _, _)) .WillOnce(Return(json_parser_return)); EXPECT_CALL(mock_orchestration_tools, doesFileExist(l4_firewall_policy_path)).WillOnce(Return(false)); - EXPECT_CALL(mock_orchestration_tools, writeFile(l4_firewall, l4_firewall_policy_path)).WillOnce(Return(true)); + EXPECT_CALL(mock_orchestration_tools, writeFile(l4_firewall, l4_firewall_policy_path, false)) + .WillOnce(Return(true)); EXPECT_CALL(mock_orchestration_status, setServiceConfiguration("l4_firewall", l4_firewall_policy_path, OrchestrationStatusConfigType::POLICY)); @@ -575,7 +578,8 @@ TEST_F(ServiceControllerTest, writeRegisteredServicesFromFile) EXPECT_CALL(mock_orchestration_tools, jsonObjectSplitter(new_configuration, _, _)) .WillOnce(Return(json_parser_return)); EXPECT_CALL(mock_orchestration_tools, doesFileExist(l4_firewall_policy_path)).WillOnce(Return(false)); - EXPECT_CALL(mock_orchestration_tools, writeFile(l4_firewall, l4_firewall_policy_path)).WillOnce(Return(true)); + EXPECT_CALL(mock_orchestration_tools, writeFile(l4_firewall, l4_firewall_policy_path, false)) + .WillOnce(Return(true)); EXPECT_CALL(mock_orchestration_status, setServiceConfiguration("l4_firewall", l4_firewall_policy_path, OrchestrationStatusConfigType::POLICY)); @@ -807,7 +811,8 @@ TEST_F(ServiceControllerTest, SettingsAndPolicyUpdateCombinations) EXPECT_CALL(mock_orchestration_tools, jsonObjectSplitter(new_configuration, _, _)) .WillOnce(Return(json_parser_return)); EXPECT_CALL(mock_orchestration_tools, doesFileExist(l4_firewall_policy_path)).WillOnce(Return(false)); - EXPECT_CALL(mock_orchestration_tools, writeFile(l4_firewall, l4_firewall_policy_path)).WillOnce(Return(true)); + EXPECT_CALL(mock_orchestration_tools, writeFile(l4_firewall, l4_firewall_policy_path, false)) + .WillOnce(Return(true)); EXPECT_CALL(mock_orchestration_status, setServiceConfiguration("l4_firewall", l4_firewall_policy_path, OrchestrationStatusConfigType::POLICY)); @@ -965,7 +970,7 @@ TEST_F(ServiceControllerTest, backup) ).WillOnce(Return(true)); EXPECT_CALL( mock_orchestration_tools, - writeFile(l4_firewall, l4_firewall_policy_path)).WillOnce(Return(true) + writeFile(l4_firewall, l4_firewall_policy_path, false)).WillOnce(Return(true) ); EXPECT_CALL(mock_orchestration_tools, copyFile(policy_file_path, policy_file_path + backup_extension)) .WillOnce(Return(true)); @@ -1078,7 +1083,7 @@ TEST_F(ServiceControllerTest, backup_file_doesnt_exist) ).WillOnce(Return(true)); EXPECT_CALL( mock_orchestration_tools, - writeFile(l4_firewall, l4_firewall_policy_path)).WillOnce(Return(true) + writeFile(l4_firewall, l4_firewall_policy_path, false)).WillOnce(Return(true) ); // backup file doesn't exist so the copyFile function should be called 0 times @@ -1194,7 +1199,7 @@ TEST_F(ServiceControllerTest, backupAttempts) EXPECT_CALL( mock_orchestration_tools, - writeFile(l4_firewall, l4_firewall_policy_path)).WillOnce(Return(true) + writeFile(l4_firewall, l4_firewall_policy_path, false)).WillOnce(Return(true) ); EXPECT_CALL(mock_orchestration_tools, copyFile(policy_file_path, policy_file_path + backup_extension)) @@ -1311,8 +1316,10 @@ TEST_F(ServiceControllerTest, MultiUpdateConfiguration) EXPECT_CALL(mock_orchestration_status, setServiceConfiguration("orchestration", orchestration_policy_path, OrchestrationStatusConfigType::POLICY)); - EXPECT_CALL(mock_orchestration_tools, writeFile(l4_firewall, l4_firewall_policy_path)).WillOnce(Return(true)); - EXPECT_CALL(mock_orchestration_tools, writeFile(orchestration, orchestration_policy_path)).WillOnce(Return(true)); + EXPECT_CALL(mock_orchestration_tools, writeFile(l4_firewall, l4_firewall_policy_path, false)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_orchestration_tools, writeFile(orchestration, orchestration_policy_path, false)) + .WillOnce(Return(true)); EXPECT_CALL(mock_orchestration_tools, copyFile(policy_file_path, policy_file_path + backup_extension)) .WillOnce(Return(true)); EXPECT_CALL(mock_orchestration_tools, copyFile(file_name, policy_file_path)).WillOnce(Return(true)); @@ -1560,7 +1567,12 @@ TEST_F(ServiceControllerTest, ErrorUpdateConfigurationRest) EXPECT_CALL(mock_orchestration_tools, jsonObjectSplitter(new_configuration, _, _)) .WillOnce(Return(json_parser_return)); EXPECT_CALL(mock_orchestration_tools, doesFileExist(l4_firewall_policy_path)).WillOnce(Return(false)); - EXPECT_CALL(mock_orchestration_tools, writeFile(l4_firewall, l4_firewall_policy_path)).WillOnce(Return(true)); + EXPECT_CALL( + mock_orchestration_tools, + writeFile( + l4_firewall, + l4_firewall_policy_path, + false)).WillOnce(Return(true)); EXPECT_CALL( mock_orchestration_status, setServiceConfiguration("l4_firewall", l4_firewall_policy_path, OrchestrationStatusConfigType::POLICY) @@ -1667,7 +1679,7 @@ TEST_F(ServiceControllerTest, errorWhileWrtingNewConfiguration) EXPECT_CALL( mock_orchestration_tools, - writeFile(l4_firewall, l4_firewall_policy_path)).WillOnce(Return(false) + writeFile(l4_firewall, l4_firewall_policy_path, false)).WillOnce(Return(false) ); EXPECT_FALSE(i_service_controller->updateServiceConfiguration(file_name, "").ok()); @@ -1782,7 +1794,7 @@ TEST_F(ServiceControllerTest, testMultitenantConfFiles) EXPECT_CALL(mock_orchestration_tools, doesFileExist(l4_firewall_policy_path_new)).WillOnce(Return(false)); - EXPECT_CALL(mock_orchestration_tools, writeFile(l4_firewall, l4_firewall_policy_path_new)) + EXPECT_CALL(mock_orchestration_tools, writeFile(l4_firewall, l4_firewall_policy_path_new, false)) .WillOnce(Return(true)); EXPECT_CALL(mock_orchestration_status, setServiceConfiguration( @@ -1889,7 +1901,7 @@ TEST_F(ServiceControllerTest, test_delayed_reconf) EXPECT_CALL(mock_orchestration_tools, jsonObjectSplitter(new_configuration, _, _)) .WillOnce(Return(json_parser_return)); EXPECT_CALL(mock_orchestration_tools, doesFileExist(l4_firewall_policy_path)).WillOnce(Return(false)); - EXPECT_CALL(mock_orchestration_tools, writeFile(l4_firewall, l4_firewall_policy_path)). + EXPECT_CALL(mock_orchestration_tools, writeFile(l4_firewall, l4_firewall_policy_path, false)). WillOnce(Return(true)); EXPECT_CALL(mock_orchestration_status, setServiceConfiguration("l4_firewall", l4_firewall_policy_path, OrchestrationStatusConfigType::POLICY)); diff --git a/components/security_apps/orchestration/update_communication/CMakeLists.txt b/components/security_apps/orchestration/update_communication/CMakeLists.txt index 94e0225..531f214 100755 --- a/components/security_apps/orchestration/update_communication/CMakeLists.txt +++ b/components/security_apps/orchestration/update_communication/CMakeLists.txt @@ -1,2 +1,2 @@ -add_library(update_communication update_communication.cc hybrid_communication.cc fog_communication.cc fog_authenticator.cc local_communication.cc declarative_policy_utils.cc) +add_library(update_communication update_communication.cc hybrid_communication.cc fog_communication.cc fog_authenticator.cc local_communication.cc declarative_policy_utils.cc fog_helper_open_source.cc) #add_subdirectory(update_communication_ut) diff --git a/components/security_apps/orchestration/update_communication/declarative_policy_utils.cc b/components/security_apps/orchestration/update_communication/declarative_policy_utils.cc index f5b5dae..52fcd44 100755 --- a/components/security_apps/orchestration/update_communication/declarative_policy_utils.cc +++ b/components/security_apps/orchestration/update_communication/declarative_policy_utils.cc @@ -16,6 +16,7 @@ USE_DEBUG_FLAG(D_ORCHESTRATOR); void DeclarativePolicyUtils::init() { + local_policy_path = getFilesystemPathConfig() + "/conf/local_policy.yaml"; should_apply_policy = true; Singleton::Consume::by()->addRestCall( RestAction::SET, "apply-policy" @@ -25,9 +26,10 @@ DeclarativePolicyUtils::init() // LCOV_EXCL_START Reason: no test exist void -DeclarativePolicyUtils::upon(const ApplyPolicyEvent &) +DeclarativePolicyUtils::upon(const ApplyPolicyEvent &event) { dbgTrace(D_ORCHESTRATOR) << "Apply policy event"; + local_policy_path = event.getPolicyPath(); should_apply_policy = true; } // LCOV_EXCL_STOP @@ -54,11 +56,9 @@ DeclarativePolicyUtils::getLocalPolicyChecksum() return orchestration_tools->readFile("/etc/cp/conf/k8s-policy-check.trigger"); } - string policy_path = Singleton::Consume::by()->getLocalPolicyPath(); - Maybe file_checksum = orchestration_tools->calculateChecksum( I_OrchestrationTools::SELECTED_CHECKSUM_TYPE, - policy_path + local_policy_path ); if (!file_checksum.ok()) { @@ -83,8 +83,11 @@ void DeclarativePolicyUtils::updateCurrentPolicy(const string &policy_checksum) { string clean_policy_checksum = getCleanChecksum(policy_checksum); - curr_policy = Singleton::Consume::by()->parsePolicy( - clean_policy_checksum + auto env = Singleton::Consume::by()->getEnvType(); + curr_policy = Singleton::Consume::by()->generateAppSecLocalPolicy( + env, + clean_policy_checksum, + local_policy_path ); } @@ -94,7 +97,7 @@ DeclarativePolicyUtils::getPolicyChecksum() I_OrchestrationTools *orchestration_tools = Singleton::Consume::by(); Maybe file_checksum = orchestration_tools->calculateChecksum( I_OrchestrationTools::SELECTED_CHECKSUM_TYPE, - Singleton::Consume::by()->getAgentPolicyPath() + "/tmp/local_appsec.policy" ); if (!file_checksum.ok()) { diff --git a/components/security_apps/orchestration/update_communication/fog_authenticator.cc b/components/security_apps/orchestration/update_communication/fog_authenticator.cc index 8522e1d..55ddfe8 100755 --- a/components/security_apps/orchestration/update_communication/fog_authenticator.cc +++ b/components/security_apps/orchestration/update_communication/fog_authenticator.cc @@ -187,6 +187,8 @@ FogAuthenticator::registerAgent( request << make_pair("managedMode", "management"); } + request << make_pair("userEdition", getUserEdition()); + if (details_resolver->isReverseProxy()) { request << make_pair("reverse_proxy", "true"); } @@ -207,6 +209,9 @@ FogAuthenticator::registerAgent( if (details_resolver->compareCheckpointVersion(8100, std::greater_equal())) { request << make_pair("isCheckpointVersionGER81", "true"); } + if (details_resolver->compareCheckpointVersion(8200, std::greater_equal())) { + request << make_pair("isCheckpointVersionGER82", "true"); + } #endif // gaia || smb auto fog_messaging = Singleton::Consume::by(); diff --git a/components/security_apps/orchestration/update_communication/fog_helper_open_source.cc b/components/security_apps/orchestration/update_communication/fog_helper_open_source.cc new file mode 100755 index 0000000..6b7de3a --- /dev/null +++ b/components/security_apps/orchestration/update_communication/fog_helper_open_source.cc @@ -0,0 +1,9 @@ +#include "fog_authenticator.h" + +#include + +std::string +FogAuthenticator::getUserEdition() const +{ + return "community"; +} diff --git a/components/security_apps/rate_limit/CMakeLists.txt b/components/security_apps/rate_limit/CMakeLists.txt new file mode 100644 index 0000000..15e7bbc --- /dev/null +++ b/components/security_apps/rate_limit/CMakeLists.txt @@ -0,0 +1,6 @@ +include_directories(../waap/waap_clib) +include_directories(../waap/include) + +add_library(rate_limit_comp rate_limit.cc) + +add_library(rate_limit_config rate_limit_config.cc) diff --git a/components/security_apps/rate_limit/rate_limit.cc b/components/security_apps/rate_limit/rate_limit.cc new file mode 100755 index 0000000..a917b6f --- /dev/null +++ b/components/security_apps/rate_limit/rate_limit.cc @@ -0,0 +1,535 @@ +#include "rate_limit.h" + +#include +#include +#include + +#include "debug.h" +#include "i_environment.h" +#include "i_mainloop.h" +#include "i_time_get.h" +#include "rate_limit_config.h" +#include "nginx_attachment_common.h" +#include "http_inspection_events.h" +#include "Waf2Util.h" +#include "generic_rulebase/evaluators/asset_eval.h" +#include "WaapConfigApi.h" +#include "WaapConfigApplication.h" +#include "PatternMatcher.h" +#include "i_waapConfig.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hiredis/hiredis.h" + +USE_DEBUG_FLAG(D_RATE_LIMIT); + +using namespace std; + +enum class RateLimitVedict { ACCEPT, DROP, DROP_AND_LOG }; + +class RateLimit::Impl + : + public Listener +{ +public: + Impl() = default; + ~Impl() = default; + + Maybe + extractUri(const string &address) + { + size_t protocolPos = address.find("://"); + if (protocolPos == std::string::npos) return genError("Invalid URI format: " + address); + + size_t domainPos = address.find('/', protocolPos + 3); + if (domainPos == std::string::npos) return string(""); + + return address.substr(domainPos); + } + + bool + isRuleMatchingUri(const string &rule_uri, const string &request_uri, bool should_rule_be_exact_match) + { + if (rule_uri.find("*") != string::npos) { + // first condition is for 'exact match with wildcard' + // second is for when the rule serves as a prefix + bool wildcard_match = + !should_rule_be_exact_match && PatternMatcherWildcard(rule_uri + "*").match(request_uri + "/"); + wildcard_match |= PatternMatcherWildcard(rule_uri).match(request_uri); + return wildcard_match; + } + + return !should_rule_be_exact_match && str_starts_with(request_uri, rule_uri); + } + + Maybe + findRateLimitRule(const string &matched_uri, string &asset_id) + { + WaapConfigAPI api_config; + WaapConfigApplication application_config; + IWaapConfig* site_config = nullptr; + + if (WaapConfigAPI::getWaapAPIConfig(api_config)) { + site_config = &api_config; + } else if (WaapConfigApplication::getWaapSiteConfig(application_config)) { + site_config = &application_config; + } + + if (site_config == nullptr) return genError("Failed to get asset configuration. Skipping rate limit check."); + + asset_id = site_config->get_AssetId(); + ScopedContext rate_limit_ctx; + rate_limit_ctx.registerValue(AssetMatcher::ctx_key, site_config->get_AssetId()); + auto maybe_rate_limit_config = getConfiguration("rulebase", "rateLimit"); + if (!maybe_rate_limit_config.ok()) + return genError("Failed to get rate limit configuration. Skipping rate limit check."); + + const auto &rate_limit_config = maybe_rate_limit_config.unpack(); + mode = rate_limit_config.getRateLimitMode(); + + if (mode == "Inactive") return genError("Rate limit mode is Inactive in policy"); + + set rule_set; + Maybe matched_rule = genError("URI did not match any rate limit rule."); + int rate_limit_longest_match = 0; + for (const auto &application_url : site_config->get_applicationUrls()) { + dbgTrace(D_RATE_LIMIT) << "Application URL: " << application_url; + + auto maybe_uri = extractUri(application_url); + if (!maybe_uri.ok()) { + dbgWarning(D_RATE_LIMIT) << "Failed to extract URI from application URL: " << maybe_uri.getErr(); + continue; + } + + string application_uri = maybe_uri.unpack(); + if (application_uri.back() == '/') application_uri.pop_back(); + + for (const auto &rule : rate_limit_config.getRateLimitRules()) { + string full_rule_uri = application_uri + rule.getRateLimitUri(); + int full_rule_uri_length = full_rule_uri.length(); + + // avoiding duplicates + if (!rule_set.insert(full_rule_uri).second) continue; + + dbgTrace(D_RATE_LIMIT) + << "Trying to match rule uri: " + << full_rule_uri + << " with request uri: " + << matched_uri; + + if (full_rule_uri_length < rate_limit_longest_match) { + dbgDebug(D_RATE_LIMIT) + << "rule is shorter then already matched rule. current rule length: " + << full_rule_uri_length + << ", previously longest matched rule length: " + << rate_limit_longest_match; + continue; + } + + if (full_rule_uri == matched_uri || + full_rule_uri == matched_uri + "/" || + full_rule_uri + "/" == matched_uri) { + dbgDebug(D_RATE_LIMIT) + << "Found Exact match to request uri: " + << matched_uri + << ", rule uri: " + << full_rule_uri; + return rule; + } + + if (rule.getRateLimitUri() == "/") { + dbgDebug(D_RATE_LIMIT) + << "Matched new longest rule, request uri: " + << matched_uri + << ", rule uri: " + << full_rule_uri; + matched_rule = rule; + rate_limit_longest_match = full_rule_uri_length; + continue; + } + + if (isRuleMatchingUri(full_rule_uri, matched_uri, rule.isExactMatch())) { + dbgDebug(D_RATE_LIMIT) + << "Matched new longest rule, request uri: " + << matched_uri + << ", rule uri: " + << full_rule_uri; + matched_rule = rule; + rate_limit_longest_match = full_rule_uri_length; + } + } + } + + return matched_rule; + } + + EventVerdict + respond(const HttpRequestHeaderEvent &event) override + { + if (!event.isLastHeader()) return INSPECT; + + auto uri_ctx = Singleton::Consume::by()->get(HttpTransactionData::uri_ctx); + if (!uri_ctx.ok()) { + dbgWarning(D_RATE_LIMIT) << "Unable to get URL from context, Not enforcing rate limit"; + return ACCEPT; + } + + string asset_id; + auto uri = uri_ctx.unpack(); + transform(uri.begin(), uri.end(), uri.begin(), [](unsigned char c) { return tolower(c); }); + auto maybe_rule = findRateLimitRule(uri, asset_id); + if (!maybe_rule.ok()) { + dbgDebug(D_RATE_LIMIT) << "Not Enforcing Rate Limit: " << maybe_rule.getErr(); + return ACCEPT; + } + + const auto &rule = maybe_rule.unpack(); + burst = rule.getRateLimit(); + limit = static_cast(rule.getRateLimit()) / (rule.getRateLimitScope() == "Minute" ? 60 : 1); + + dbgTrace(D_RATE_LIMIT) + << "found rate limit rule with: " + << rule.getRateLimit() + << " per " + << (rule.getRateLimitScope() == "Minute" ? 60 : 1) + << " seconds"; + + auto maybe_source_identifier = + Singleton::Consume::by()->get(HttpTransactionData::source_identifier); + if (!maybe_source_identifier.ok()) { + dbgWarning(D_RATE_LIMIT) << "Unable to get source identifier from context, not enforcing rate limit"; + return ACCEPT; + } + + auto &source_identifier = maybe_source_identifier.unpack(); + dbgDebug(D_RATE_LIMIT) << "source identifier value: " << source_identifier; + + string unique_key = asset_id + ":" + source_identifier + ":" + uri; + if (unique_key.back() == '/') unique_key.pop_back(); + + auto verdict = decide(unique_key); + if (verdict == RateLimitVedict::ACCEPT) { + dbgTrace(D_RATE_LIMIT) << "Received ACCEPT verdict."; + return ACCEPT; + } + + if (verdict == RateLimitVedict::DROP_AND_LOG) sendLog(uri, source_identifier, rule); + + if (mode == "Active") { + dbgTrace(D_RATE_LIMIT) << "Received DROP verdict, this request will be blocked by rate limit"; + return DROP; + } + + dbgTrace(D_RATE_LIMIT) << "Received DROP in detect mode, will not block."; + return ACCEPT; + } + + string + getListenerName() const override + { + return "rate limit"; + } + + RateLimitVedict + decide(const std::string &key) { + if (redis == nullptr) { + dbgDebug(D_RATE_LIMIT) + << "there is no connection to the redis at the moment, unable to enforce rate limit"; + reconnectRedis(); + return RateLimitVedict::ACCEPT; + } + + redisReply* reply = static_cast(redisCommand(redis, "EVALSHA %s 1 %s %f %d", + rate_limit_lua_script_hash.c_str(), key.c_str(), limit, burst)); + + if (reply == NULL || redis->err) { + dbgDebug(D_RATE_LIMIT) + << "Error executing Redis command: No reply received, unable to enforce rate limit"; + reconnectRedis(); + return RateLimitVedict::ACCEPT; + } + + // redis's lua script returned true - accept + if (reply->type == REDIS_REPLY_INTEGER) { + freeReplyObject(reply); + return RateLimitVedict::ACCEPT; + } + + // redis's lua script returned false - drop, no need to log + if (reply->type == REDIS_REPLY_NIL) { + freeReplyObject(reply); + return RateLimitVedict::DROP; + } + + // redis's lua script returned string - drop and send log + const char* log_str = "BLOCK AND LOG"; + if (reply->type == REDIS_REPLY_STRING && strncmp(reply->str, log_str, strlen(log_str)) == 0) { + freeReplyObject(reply); + return RateLimitVedict::DROP_AND_LOG; + } + + dbgDebug(D_RATE_LIMIT) + << "Got unexected reply from redis. reply type: " + << reply->type + << ". not enforcing rate limit for this request."; + freeReplyObject(reply); + return RateLimitVedict::ACCEPT; + } + + void + sendLog(const string &uri, const string &source_identifier, const RateLimitRule& rule) + { + set rate_limit_triggers_set; + for (const auto &trigger : rule.getRateLimitTriggers()) { + rate_limit_triggers_set.insert(trigger.getTriggerId()); + } + + ScopedContext ctx; + ctx.registerValue>(TriggerMatcher::ctx_key, rate_limit_triggers_set); + auto log_trigger = getConfigurationWithDefault(LogTriggerConf(), "rulebase", "log"); + + if (!log_trigger.isPreventLogActive(LogTriggerConf::SecurityType::AccessControl)) { + dbgTrace(D_RATE_LIMIT) << "Not sending rate-limit log as it is not required"; + return; + } + + auto maybe_rule_by_ctx = getConfiguration("rulebase", "rulesConfig"); + if (!maybe_rule_by_ctx.ok()) { + dbgWarning(D_RATE_LIMIT) + << "rule was not found by the given context. Reason: " + << maybe_rule_by_ctx.getErr(); + return; + } + + string event_name = "Rate limit"; + + LogGen log = log_trigger( + event_name, + LogTriggerConf::SecurityType::AccessControl, + ReportIS::Severity::HIGH, + ReportIS::Priority::HIGH, + true, + LogField("practiceType", "Rate Limit"), + ReportIS::Tags::RATE_LIMIT + ); + + const auto &rule_by_ctx = maybe_rule_by_ctx.unpack(); + + log + << LogField("assetId", rule_by_ctx.getAssetId()) + << LogField("assetName", rule_by_ctx.getAssetName()) + << LogField("ruleId", rule_by_ctx.getRuleId()) + << LogField("ruleName", rule_by_ctx.getRuleName()) + << LogField("httpUriPath", uri) + << LogField("httpSourceId", source_identifier) + << LogField("securityAction", (mode == "Active" ? "Prevent" : "Detect")) + << LogField("waapIncidentType", "Rate Limit"); + + auto http_method = + Singleton::Consume::by()->get(HttpTransactionData::method_ctx); + if (http_method.ok()) log << LogField("httpMethod", http_method.unpack()); + + auto http_host = + Singleton::Consume::by()->get(HttpTransactionData::host_name_ctx); + if (http_host.ok()) log << LogField("httpHostName", http_host.unpack()); + + auto source_ip = + Singleton::Consume::by()->get(HttpTransactionData::client_ip_ctx); + if (source_ip.ok()) log << LogField("sourceIP", ipAddrToStr(source_ip.unpack())); + + auto proxy_ip = + Singleton::Consume::by()->get(HttpTransactionData::proxy_ip_ctx); + if (proxy_ip.ok() && source_ip.ok() && ipAddrToStr(source_ip.unpack()) != proxy_ip.unpack()) { + log << LogField("proxyIP", static_cast(proxy_ip.unpack())); + } + } + + string + ipAddrToStr(const IPAddr& ip_address) const + { + char str[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &(ip_address), str, INET_ADDRSTRLEN); + return string(str); + } + + Maybe + connectRedis() + { + disconnectRedis(); + + redisOptions options; + memset(&options, 0, sizeof(redisOptions)); + REDIS_OPTIONS_SET_TCP( + &options, + "127.0.0.1", + getConfigurationWithDefault(6379, "connection", "Redis Port") + ); + + timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = getConfigurationWithDefault(30000, "connection", "Redis Timeout"); + options.connect_timeout = &timeout; + options.command_timeout = &timeout; + + redisContext* context = redisConnectWithOptions(&options); + if (context != nullptr && context->err) { + dbgDebug(D_RATE_LIMIT) + << "Error connecting to Redis: " + << context->errstr; + redisFree(context); + return genError(""); + } + + if (context == nullptr) return genError(""); + + redis = context; + static string luaScript = R"( + local key = KEYS[1] + local rateLimit = tonumber(ARGV[1]) + local burstLimit = tonumber(ARGV[2]) + local currentTimeSeconds = tonumber(redis.call('time')[1]) + local lastRequestTimeSeconds = tonumber(redis.call('get', key .. ':lastRequestTime') or "0") + local elapsedTimeSeconds = currentTimeSeconds - lastRequestTimeSeconds + local tokens = tonumber(redis.call('get', key .. ':tokens') or burstLimit) + local was_blocked = tonumber(redis.call('get', key .. ':block') or "0") + + tokens = math.min(tokens + (elapsedTimeSeconds * rateLimit), burstLimit) + + if tokens >= 1 then + tokens = tokens - 1 + redis.call('set', key .. ':tokens', tokens) + redis.call('set', key .. ':lastRequestTime', currentTimeSeconds) + redis.call('expire', key .. ':tokens', 60) + redis.call('expire', key .. ':lastRequestTime', 60) + return true + elseif was_blocked == 1 then + redis.call('set', key .. ':block', 1) + redis.call('expire', key .. ':block', 60) + return false + else + redis.call('set', key .. ':block', 1) + redis.call('expire', key .. ':block', 60) + return "BLOCK AND LOG" + end + )"; + + // Load the Lua script in Redis and retrieve its SHA1 hash + redisReply* loadReply = + static_cast(redisCommand(redis, "SCRIPT LOAD %s", luaScript.c_str())); + if (loadReply != nullptr && loadReply->type == REDIS_REPLY_STRING) { + rate_limit_lua_script_hash = loadReply->str; + freeReplyObject(loadReply); + } + + return Maybe(); + } + + void + reconnectRedis() + { + dbgFlow(D_RATE_LIMIT) << "Trying to reconnect to redis after failure to invoke a redis command"; + static bool is_reconnecting = false; + if (!is_reconnecting) { + is_reconnecting = true; + Singleton::Consume::by()->addOneTimeRoutine( + I_MainLoop::RoutineType::System, + [this] () + { + connectRedis(); + is_reconnecting = false; + }, + "Reconnect redis", + false + ); + } + + } + + void + handleNewPolicy() + { + if (RateLimitConfig::isActive() && !redis) { + connectRedis(); + registerListener(); + return; + } + + if (!RateLimitConfig::isActive()) { + disconnectRedis(); + unregisterListener(); + } + } + + void + disconnectRedis() + { + if (redis) { + redisFree(redis); + redis = nullptr; + } + } + + void + init() + { + Singleton::Consume::by()->addOneTimeRoutine( + I_MainLoop::RoutineType::System, + [this] () + { + handleNewPolicy(); + registerConfigLoadCb([this]() { handleNewPolicy(); }); + }, + "Initialize rate limit component", + false + ); + } + + void + fini() + { + disconnectRedis(); + } + +private: + static constexpr auto DROP = ngx_http_cp_verdict_e::TRAFFIC_VERDICT_DROP; + static constexpr auto ACCEPT = ngx_http_cp_verdict_e::TRAFFIC_VERDICT_ACCEPT; + static constexpr auto INSPECT = ngx_http_cp_verdict_e::TRAFFIC_VERDICT_INSPECT; + + string mode; + string rate_limit_lua_script_hash; + int burst; + float limit; + redisContext* redis = nullptr; +}; + +RateLimit::RateLimit() : Component("RateLimit"), pimpl(make_unique()) {} + +RateLimit::~RateLimit() = default; + +void +RateLimit::preload() +{ + registerExpectedConfiguration("WAAP", "WebApplicationSecurity"); + registerExpectedConfiguration("WAAP", "WebAPISecurity"); + registerExpectedConfigFile("waap", Config::ConfigFileType::Policy); + registerExpectedConfiguration("rulebase", "rateLimit"); + registerExpectedConfigFile("accessControlV2", Config::ConfigFileType::Policy); + registerConfigPrepareCb([]() { RateLimitConfig::resetIsActive(); }); +} + +void +RateLimit::init() { pimpl->init(); } + +void +RateLimit::fini() { pimpl->fini(); } diff --git a/components/security_apps/rate_limit/rate_limit_config.cc b/components/security_apps/rate_limit/rate_limit_config.cc new file mode 100755 index 0000000..7084320 --- /dev/null +++ b/components/security_apps/rate_limit/rate_limit_config.cc @@ -0,0 +1,157 @@ +#include "rate_limit_config.h" + +bool RateLimitConfig::is_active = false; + +void +RateLimitTrigger::load(cereal::JSONInputArchive &ar) +{ + dbgTrace(D_REVERSE_PROXY) << "Serializing single Rate Limit rule's triggers"; + try { + ar(cereal::make_nvp("id", id)); + } catch (const cereal::Exception &e) { + dbgWarning(D_REVERSE_PROXY) + << "Failed to load single Rate Limit JSON rule's triggers. Error: " << e.what(); + ar.setNextName(nullptr); + } +} + +void +RateLimitRule::load(cereal::JSONInputArchive &ar) +{ + dbgTrace(D_REVERSE_PROXY) << "Serializing single Rate Limit rule"; + try { + ar(cereal::make_nvp("URI", uri)); + ar(cereal::make_nvp("scope", scope)); + ar(cereal::make_nvp("limit", limit)); + ar(cereal::make_nvp("triggers", rate_limit_triggers)); + } catch (const cereal::Exception &e) { + dbgWarning(D_REVERSE_PROXY) << "Failed to load single Rate Limit JSON rule. Error: " << e.what(); + ar.setNextName(nullptr); + } +} + +void +RateLimitRule::prepare(const std::string &asset_id, int zone_id) +{ + std::string zone_id_s = std::to_string(zone_id); + std::string zone; + if (isRootLocation()) { + zone = "root_zone_" + asset_id + "_" + zone_id_s; + } else { + std::string zone_name_suffix = uri; + std::replace(zone_name_suffix.begin(), zone_name_suffix.end(), '/', '_'); + zone = "zone" + zone_name_suffix + "_" + zone_id_s; + } + + limit_req_template_value = "zone=" + zone + " burst=" + std::to_string(limit) + " nodelay"; + + // nginx conf will look like: limit_req_zone zone=_:10m rate=r/; + std::string rate_unit = scope == "Minute" ? "r/m" : "r/s"; + limit_req_zone_template_value = + "zone=" + zone + ":" + cache_size + " rate=" + std::to_string(limit) + rate_unit; + + dbgTrace(D_REVERSE_PROXY) + << "limit_req_zone nginx template value: " + << limit_req_zone_template_value + << ", limit_req nginx template value: " + << limit_req_template_value; +} + +bool +RateLimitRule::isRootLocation() const +{ + if (uri.empty()) { + return false; + } + + auto non_root = uri.find_first_not_of("/"); + if (non_root != std::string::npos) { + return false; + } + return true; +} + +void +RateLimitConfig::load(cereal::JSONInputArchive &ar) +{ + dbgTrace(D_REVERSE_PROXY) << "Serializing Rate Limit config"; + try { + ar(cereal::make_nvp("rules", rate_limit_rules)); + ar(cereal::make_nvp("mode", mode)); + prepare(); + } catch (const cereal::Exception &e) { + dbgWarning(D_REVERSE_PROXY) << "Failed to load single Rate Limit JSON config. Error: " << e.what(); + ar.setNextName(nullptr); + } +} + +void +RateLimitConfig::addSiblingRateLimitRule(RateLimitRule &rule) { + rule.setExactMatch(); + RateLimitRule sibling_rule(rule); + sibling_rule.appendSlash(); + sibling_rule.setExactMatch(); + rate_limit_rules.push_back(sibling_rule); +} + +void +RateLimitConfig::prepare() +{ + // Removes invalid rules + auto last_valid_rule = + std::remove_if( + rate_limit_rules.begin(), + rate_limit_rules.end(), + [](const RateLimitRule &rule) { return !rule; } + ); + + rate_limit_rules.erase(last_valid_rule, rate_limit_rules.end()); + + // Removes duplicates + sort(rate_limit_rules.begin(), rate_limit_rules.end()); + rate_limit_rules.erase(std::unique(rate_limit_rules.begin(), rate_limit_rules.end()), rate_limit_rules.end()); + + std::for_each( + rate_limit_rules.begin(), + rate_limit_rules.end(), + [this](RateLimitRule &rule) { if (rule.isExactMatch()) { addSiblingRateLimitRule(rule); } } + ); + + dbgTrace(D_REVERSE_PROXY) + << "Final rate-limit rules: " + << makeSeparatedStr(rate_limit_rules, "; ") + << "; Mode: " + << mode; + + setIsActive(mode != "Inactive"); +} + +const RateLimitRule +RateLimitConfig::findLongestMatchingRule(const std::string &nginx_uri) const +{ + dbgFlow(D_REVERSE_PROXY) << "Trying to find a matching rat-limit rule for NGINX URI: " << nginx_uri; + + size_t longest_len = 0; + RateLimitRule longest_matching_rule; + for (const RateLimitRule &rule : rate_limit_rules) { + if (rule.getRateLimitUri() == nginx_uri) { + dbgTrace(D_REVERSE_PROXY) << "Found exact rate-limit match: " << rule; + return rule; + } + + if (nginx_uri.size() < rule.getRateLimitUri().size()) { + continue; + } + + if (std::equal(rule.getRateLimitUri().rbegin(), rule.getRateLimitUri().rend(), nginx_uri.rbegin())) { + if (rule.getRateLimitUri().size() > longest_len) { + longest_matching_rule = rule; + longest_len = rule.getRateLimitUri().size(); + dbgTrace(D_REVERSE_PROXY) << "Longest matching rate-limit rule so far: " << rule; + } + } + } + + dbgTrace(D_REVERSE_PROXY) << "Longest matching rate-limit rule: " << longest_matching_rule; + return longest_matching_rule; +} diff --git a/components/security_apps/waap/include/i_waapConfig.h b/components/security_apps/waap/include/i_waapConfig.h index 27eecbc..b8a1b4f 100755 --- a/components/security_apps/waap/include/i_waapConfig.h +++ b/components/security_apps/waap/include/i_waapConfig.h @@ -52,6 +52,7 @@ public: virtual const std::string& get_RuleName() const = 0; virtual const bool& get_WebAttackMitigation() const = 0; virtual const std::string& get_WebAttackMitigationAction() const = 0; + virtual const std::vector & get_applicationUrls() const = 0; virtual const std::shared_ptr& get_OverridePolicy() const = 0; virtual const std::shared_ptr& get_TriggerPolicy() const = 0; diff --git a/components/security_apps/waap/waap_clib/WaapConfigBase.cc b/components/security_apps/waap/waap_clib/WaapConfigBase.cc index 9d42f6a..910083d 100755 --- a/components/security_apps/waap/waap_clib/WaapConfigBase.cc +++ b/components/security_apps/waap/waap_clib/WaapConfigBase.cc @@ -253,6 +253,12 @@ void WaapConfigBase::loadOpenRedirectPolicy(cereal::JSONInputArchive& ar) } +const std::vector & +WaapConfigBase::get_applicationUrls() const +{ + return m_applicationUrls; +} + void WaapConfigBase::loadErrorDisclosurePolicy(cereal::JSONInputArchive& ar) { std::string failMessage = "Failed to load the WAAP Information Disclosure policy"; diff --git a/components/security_apps/waap/waap_clib/WaapConfigBase.h b/components/security_apps/waap/waap_clib/WaapConfigBase.h index 2cae5f2..55d3fd1 100755 --- a/components/security_apps/waap/waap_clib/WaapConfigBase.h +++ b/components/security_apps/waap/waap_clib/WaapConfigBase.h @@ -45,6 +45,7 @@ public: virtual const std::string& get_RuleName() const; virtual const bool& get_WebAttackMitigation() const; virtual const std::string& get_WebAttackMitigationAction() const; + virtual const std::vector & get_applicationUrls() const; virtual const std::shared_ptr& get_OverridePolicy() const; virtual const std::shared_ptr& get_TriggerPolicy() const; diff --git a/core/agent_core_utilities/agent_core_utilities.cc b/core/agent_core_utilities/agent_core_utilities.cc index 9488d32..44fe109 100755 --- a/core/agent_core_utilities/agent_core_utilities.cc +++ b/core/agent_core_utilities/agent_core_utilities.cc @@ -21,6 +21,7 @@ #include #include #include +#include #include "debug.h" @@ -49,6 +50,24 @@ exists(const string &path) return false; } +bool +isDirectory(const string &path) +{ + dbgFlow(D_INFRA_UTILS) << "Checking if path is a directory. Path: " << path; + struct stat buffer; + if (stat(path.c_str(), &buffer) != 0) { + dbgTrace(D_INFRA_UTILS) << "Path does not exists. Path: " << path; + return false; + } + + if (buffer.st_mode & S_IFDIR) { + dbgTrace(D_INFRA_UTILS) << "Path is a directory. Path: " << path; + return true; + } + + return false; +} + bool makeDir(const string &path, mode_t permission) { @@ -356,4 +375,20 @@ regexReplace(const char *file, int line, const string &sample, const regex ®e }// namespace Regex +namespace Strings +{ + +string +removeTrailingWhitespaces(string str) +{ + str.erase( + find_if(str.rbegin(), str.rend(), [] (char c) { return !isspace(c); }).base(), + str.end() + ); + + return str; +} + +} // namespace Strings + } // namespace NGEN diff --git a/core/agent_core_utilities/agent_core_utilities_ut/agent_core_utilities_ut.cc b/core/agent_core_utilities/agent_core_utilities_ut/agent_core_utilities_ut.cc index 5476f13..bbf86ac 100644 --- a/core/agent_core_utilities/agent_core_utilities_ut/agent_core_utilities_ut.cc +++ b/core/agent_core_utilities/agent_core_utilities_ut/agent_core_utilities_ut.cc @@ -98,8 +98,20 @@ TEST_F(AgentCoreUtilUT, printTest) EXPECT_EQ(NGEN::Filesystem::convertToHumanReadable(1024*gigabyte), "1024.00 GB"); } - TEST_F(AgentCoreUtilUT, fileBasenameTest) { EXPECT_EQ(NGEN::Filesystem::getFileName("/test/base/file/name"), "name"); } + +TEST_F(AgentCoreUtilUT, isDirectoryTest) +{ + mkdir("./test", 0400); + EXPECT_EQ(NGEN::Filesystem::isDirectory("/test/base/file/name"), false); + EXPECT_EQ(NGEN::Filesystem::isDirectory("./test"), true); +} + +TEST_F(AgentCoreUtilUT, removeTrailingWhitespacesTest) +{ + string str_with_trailing_whitespace = "str_with_trailing_whitespace\n\n\n\r \n\n\r"; + EXPECT_EQ(NGEN::Strings::removeTrailingWhitespaces(str_with_trailing_whitespace), "str_with_trailing_whitespace"); +} diff --git a/core/environment/environment.cc b/core/environment/environment.cc index 589f4d5..4c4af12 100644 --- a/core/environment/environment.cc +++ b/core/environment/environment.cc @@ -141,7 +141,6 @@ Environment::Impl::init() void Environment::Impl::fini() { - global.deactivate(); } void diff --git a/core/include/services_sdk/resources/report/report_enums.h b/core/include/services_sdk/resources/report/report_enums.h index 6285eea..6421991 100755 --- a/core/include/services_sdk/resources/report/report_enums.h +++ b/core/include/services_sdk/resources/report/report_enums.h @@ -143,7 +143,8 @@ enum class Notification { SDWAN_POLICY_UPDATE, SDWAN_POLICY_UPDATE_ERROR, SDWAN_POLICY_UPDATE_LOG, - SDWAN_POLICY_UPDATE_ERROR_LOG + SDWAN_POLICY_UPDATE_ERROR_LOG, + SDWAN_POLICY_WARNING_LOG }; enum class IssuingEngine { diff --git a/core/include/services_sdk/utilities/agent_core_utilities.h b/core/include/services_sdk/utilities/agent_core_utilities.h index 19b0e67..b4bb353 100755 --- a/core/include/services_sdk/utilities/agent_core_utilities.h +++ b/core/include/services_sdk/utilities/agent_core_utilities.h @@ -25,6 +25,7 @@ namespace Filesystem { bool exists(const std::string &path); +bool isDirectory(const std::string &path); bool makeDir(const std::string &path, mode_t permission = S_IRWXU); bool makeDirRecursive(const std::string &path, mode_t permission = S_IRWXU); @@ -75,6 +76,13 @@ regexReplace( } // namespace Regex +namespace Strings +{ + +std::string removeTrailingWhitespaces(std::string str); + +} // namespace Strings + } // namespace NGEN #endif // __AGENT_CORE_UTILITIES_H__ diff --git a/core/logging/log_generator.cc b/core/logging/log_generator.cc index 7977c21..b79111d 100755 --- a/core/logging/log_generator.cc +++ b/core/logging/log_generator.cc @@ -19,7 +19,10 @@ extern const string unnamed_service; LogGen::~LogGen() { - if (send_log) Singleton::Consume::by()->sendLog(log); + try { + if (send_log) Singleton::Consume::by()->sendLog(log); + } catch (...) { + } } LogGen & diff --git a/core/report/tag_and_enum_management.cc b/core/report/tag_and_enum_management.cc index 81b3b9e..13b991b 100755 --- a/core/report/tag_and_enum_management.cc +++ b/core/report/tag_and_enum_management.cc @@ -252,6 +252,7 @@ TagAndEnumManagement::convertToString(const Notification ¬ification) case Notification::SDWAN_POLICY_UPDATE_ERROR: return "8d2db6ea-30b7-11ec-8d3d-0242ac130003"; case Notification::SDWAN_POLICY_UPDATE_LOG: return "97cb79e1-e873-4f28-b123-5e19f8dd6f99"; case Notification::SDWAN_POLICY_UPDATE_ERROR_LOG: return "44ca5755-07a2-483c-b756-b7df444e175c"; + case Notification::SDWAN_POLICY_WARNING_LOG: return "c58d490e-6aa0-43da-bfaa-7edad0a57b7a"; } dbgAssert(false) << "Reached impossible notification value of: " << static_cast(notification); diff --git a/nodes/http_transaction_handler/CMakeLists.txt b/nodes/http_transaction_handler/CMakeLists.txt index ba6907e..cf999e2 100755 --- a/nodes/http_transaction_handler/CMakeLists.txt +++ b/nodes/http_transaction_handler/CMakeLists.txt @@ -14,6 +14,7 @@ target_link_libraries(cp-nano-http-transaction-handler pcre2-8 pcre2-posix yajl_s + hiredis -lshmem_ipc -lnginx_attachment_util @@ -32,6 +33,8 @@ target_link_libraries(cp-nano-http-transaction-handler waap waap_clib reputation + rate_limit_comp + rate_limit_config ips keywords l7_access_control @@ -53,6 +56,12 @@ execute_process ( ) install(FILES ${pcre2-posix} DESTINATION http_transaction_handler_service/lib) +execute_process ( + COMMAND bash -c "find /usr/lib -name \"libhiredis.so*\" | awk '{printf $0\";\"}'" + OUTPUT_VARIABLE hiredis +) +install(FILES ${hiredis} DESTINATION http_transaction_handler_service/lib) + execute_process ( COMMAND bash -c "find /usr/lib -name \"libxml2.so*\" | awk '{printf \$0\";\"}'" OUTPUT_VARIABLE xml2 diff --git a/nodes/http_transaction_handler/main.cc b/nodes/http_transaction_handler/main.cc index d8095a4..a4dd49a 100755 --- a/nodes/http_transaction_handler/main.cc +++ b/nodes/http_transaction_handler/main.cc @@ -17,6 +17,7 @@ #include "gradual_deployment.h" #include "http_manager.h" #include "layer_7_access_control.h" +#include "rate_limit.h" #include "waap.h" #include "ips_comp.h" #include "keyword_comp.h" @@ -30,6 +31,7 @@ main(int argc, char **argv) GradualDeployment, HttpManager, Layer7AccessControl, + RateLimit, WaapComponent, IPSComp, KeywordComp diff --git a/nodes/orchestration/package/open-appsec-ctl.sh b/nodes/orchestration/package/open-appsec-ctl.sh index 637570c..8cd0294 100644 --- a/nodes/orchestration/package/open-appsec-ctl.sh +++ b/nodes/orchestration/package/open-appsec-ctl.sh @@ -964,11 +964,13 @@ run_status() # Initials - rs if echo "$rs_orch_status" | grep -q "update status"; then rs_line_count=$(echo "$rs_orch_status" | grep -c '^') rs_policy_load_time="$(echo "${rs_orch_status}" | grep "Last policy update"| sed "s|\"||g" | sed "s|,||g")" + rs_ai_model_ver="$(echo "${rs_orch_status}" | grep "AI model version"| sed "s|\"||g" | sed "s|,||g")" rs_temp_old_status=$(echo "$rs_orch_status" | sed -r "${rs_line_count},${rs_line_count}d; "' 1,1d; s/^\s*//g; s/^\n//g; s/\"//g; s/\\n/\n/g; s/\,//g') else rs_temp_old_status=$(sed 's/{//g' <${FILESYSTEM_PATH}/$cp_nano_conf_location/orchestration_status.json | sed 's/}//g' | sed 's/"//g' | sed 's/,//g' | sed -r '/^\s*$/d' | sed -r 's/^ //g') rs_policy_load_time="$(cat ${FILESYSTEM_PATH}/conf/orchestration_status.json | grep "Last policy update" | sed "s|\"||g" | sed "s|,||g")" + rs_ai_model_ver="$(cat ${FILESYSTEM_PATH}/conf/orchestration_status.json | grep "AI model version" | sed "s|\"||g" | sed "s|,||g")" fi if [ -n "$(cat ${FILESYSTEM_PATH}/conf/agent_details.json | grep "hybrid_mode")" ]; then @@ -1008,6 +1010,7 @@ run_status() # Initials - rs fi echo "Policy load status: ${rs_policy_load_status}" echo ${rs_policy_load_time} + echo ${rs_ai_model_ver} echo "" for service in $all_services; do @@ -1261,7 +1264,7 @@ run_ai() # Initials - ra else ra_orch_status=$(curl_func "$(extract_api_port orchestration)"/show-orchestration-status) if ! echo "$ra_orch_status" | grep -q "update status"; then - ra_orch_status=$(cat ${FILESYSTEM_PATH}/$cp_nano_conf_location/orchestration_status.json) + [ -f ${FILESYSTEM_PATH}/$cp_nano_conf_location/orchestrations_status.json ] && ra_orch_status=$(cat ${FILESYSTEM_PATH}/$cp_nano_conf_location/orchestration_status.json) fi if [ -n "${ra_orch_status}" ]; then ra_fog_address=$(printf "%s" "$ra_orch_status" | grep "Fog address" | cut -d '"' -f4) diff --git a/nodes/orchestration/package/orchestration_package.sh b/nodes/orchestration/package/orchestration_package.sh index 47e2457..8ab2fcb 100755 --- a/nodes/orchestration/package/orchestration_package.sh +++ b/nodes/orchestration/package/orchestration_package.sh @@ -439,6 +439,28 @@ cp_copy() # Initials - cc cp_print "Destination md5, after the copy:\n$DEST_AFTER_COPY" } +update_cloudguard_appsec_manifest() +{ + if [ -z ${CLOUDGUARD_APPSEC_STANDALONE} ] || [ -z ${DOCKER_RPM_ENABLED} ]; then + return + fi + + selected_cloudguard_appsec_manifest_path="/tmp/cloudguard_appsec_manifest.json" + if [ "${DOCKER_RPM_ENABLED}" = "false" ]; then + selected_cloudguard_appsec_manifest_path="/tmp/self_managed_cloudguard_appsec_manifest.json" + fi + + if [ ! -f "$selected_cloudguard_appsec_manifest_path" ]; then + return + fi + + cloudguard_appsec_manifest_path="${selected_cloudguard_appsec_manifest_path}.used" + mv "$selected_cloudguard_appsec_manifest_path" "$cloudguard_appsec_manifest_path" + fog_host=$(echo "$var_fog_address" | sed 's/https\?:\/\///') + fog_host=${fog_host%/} + sed "s/namespace/${fog_host}/g" ${cloudguard_appsec_manifest_path} > "${FILESYSTEM_PATH}/${CONF_PATH}/manifest.json" +} + install_watchdog_gaia() { # verify that DB is clean from cp-nano-watchdog @@ -907,6 +929,8 @@ install_orchestration() cp_exec "mkdir -p ${LOG_FILE_PATH}/${LOG_PATH}" cp_exec "mkdir -p ${FILESYSTEM_PATH}/${DATA_PATH}" + update_cloudguard_appsec_manifest + if [ ! -f ${FILESYSTEM_PATH}/${DEFAULT_SETTINGS_PATH} ]; then echo "{\"agentSettings\": []}" > ${FILESYSTEM_PATH}/${DEFAULT_SETTINGS_PATH} fi