diff --git a/CMakeLists.txt b/CMakeLists.txt index 41fe9be..e034185 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ project (ngen) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -Wall -Wno-terminate") execute_process(COMMAND grep -c "Alpine Linux" /etc/os-release OUTPUT_VARIABLE IS_ALPINE) -if(IS_ALPINE EQUAL "1") +if(NOT IS_ALPINE EQUAL "0") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Dalpine") endif() diff --git a/components/include/i_update_communication.h b/components/include/i_update_communication.h index b31d8fd..0d282b0 100755 --- a/components/include/i_update_communication.h +++ b/components/include/i_update_communication.h @@ -26,6 +26,7 @@ using OrchData = Maybe; class I_UpdateCommunication { public: + virtual void init() = 0; virtual Maybe sendPolicyVersion( const std::string &policy_version, const std::string &policy_versions diff --git a/components/security_apps/local_policy_mgmt_gen/CMakeLists.txt b/components/security_apps/local_policy_mgmt_gen/CMakeLists.txt index d4f060c..2333a1f 100644 --- a/components/security_apps/local_policy_mgmt_gen/CMakeLists.txt +++ b/components/security_apps/local_policy_mgmt_gen/CMakeLists.txt @@ -13,6 +13,7 @@ add_library(local_policy_mgmt_gen local_policy_mgmt_gen.cc new_appsec_policy_crd_parser.cc new_appsec_linux_policy.cc + new_auto_upgrade.cc new_custom_response.cc new_trusted_sources.cc new_log_trigger.cc diff --git a/components/security_apps/local_policy_mgmt_gen/access_control_practice.cc b/components/security_apps/local_policy_mgmt_gen/access_control_practice.cc index 329a4b6..09aaab1 100755 --- a/components/security_apps/local_policy_mgmt_gen/access_control_practice.cc +++ b/components/security_apps/local_policy_mgmt_gen/access_control_practice.cc @@ -18,16 +18,12 @@ using namespace std; USE_DEBUG_FLAG(D_LOCAL_POLICY); // LCOV_EXCL_START Reason: no test exist -static const set valid_modes = {"prevent", "detect", "inactive"}; -static const set valid_units = {"minute", "second"}; - -static const std::unordered_map key_to_mode_val = { - { "prevent-learn", "Prevent"}, - { "detect-learn", "Detect"}, - { "prevent", "Prevent"}, - { "detect", "Detect"}, - { "inactive", "Inactive"} +static const map valid_modes_to_key = { + {"prevent", "Active"}, + {"detect", "Detect"}, + {"inactive", "Inactive"} }; +static const set valid_units = {"minute", "second"}; static const std::unordered_map key_to_units_val = { { "second", "Second"}, @@ -78,7 +74,7 @@ RateLimitSection::RateLimitSection( { bool any = asset_name == "Any" && url == "Any" && uri == "Any"; string asset_id = any ? "Any" : url+uri; - context = "assetId(" + asset_id + ")"; + context = any ? "All()" : "assetId(" + asset_id + ")"; } void @@ -86,7 +82,7 @@ RateLimitSection::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("mode", mode), cereal::make_nvp("practiceId", practice_id), cereal::make_nvp("name", name), cereal::make_nvp("rules", rules) @@ -180,9 +176,13 @@ void AccessControlRateLimit::load(cereal::JSONInputArchive &archive_in) { dbgTrace(D_LOCAL_POLICY) << "Loading Access control rate limit"; - parseAppsecJSONKey("overrideMode", mode, archive_in, "Inactive"); - if (valid_modes.count(mode) == 0) { - dbgWarning(D_LOCAL_POLICY) << "AppSec access control rate limit override mode invalid: " << mode; + string in_mode; + parseAppsecJSONKey("overrideMode", in_mode, archive_in, "inactive"); + if (valid_modes_to_key.find(in_mode) == valid_modes_to_key.end()) { + dbgWarning(D_LOCAL_POLICY) << "AppSec access control rate limit override mode invalid: " << in_mode; + mode = "Inactive"; + } else { + mode = valid_modes_to_key.at(in_mode); } parseAppsecJSONKey>("rules", rules, archive_in); } diff --git a/components/security_apps/local_policy_mgmt_gen/appsec_practice_section.cc b/components/security_apps/local_policy_mgmt_gen/appsec_practice_section.cc index 7b9a406..bb00d72 100755 --- a/components/security_apps/local_policy_mgmt_gen/appsec_practice_section.cc +++ b/components/security_apps/local_policy_mgmt_gen/appsec_practice_section.cc @@ -12,6 +12,7 @@ // limitations under the License. #include "appsec_practice_section.h" +#include using namespace std; @@ -238,6 +239,7 @@ AppSecPracticeOpenSchemaAPI::getConfigMap() const { return config_map; } + // LCOV_EXCL_STOP void AppSecPracticeSpec::load(cereal::JSONInputArchive &archive_in) @@ -272,6 +274,7 @@ AppSecPracticeSpec::getSnortSignatures() const { return snort_signatures; } + // LCOV_EXCL_STOP const AppSecPracticeWebAttacks & @@ -337,6 +340,7 @@ ParsedMatch::ParsedMatch(const ExceptionMatch &exceptions) parsed_match.push_back(ParsedMatch(exception_match)); } } + // LCOV_EXCL_STOP void @@ -375,6 +379,7 @@ AppSecOverride::AppSecOverride(const InnerException &parsed_exceptions) map behavior = {{parsed_exceptions.getBehaviorKey(), parsed_exceptions.getBehaviorValue()}}; parsed_behavior.push_back(behavior); } + // LCOV_EXCL_STOP void @@ -426,10 +431,11 @@ WebAppSection::WebAppSection( web_attack_mitigation_mode(parsed_appsec_spec.getWebAttacks().getMode(default_mode)), practice_advanced_config(parsed_appsec_spec), anti_bots(parsed_appsec_spec.getAntiBot()), - trusted_sources({parsed_trusted_sources}) + trusted_sources({ parsed_trusted_sources }) { web_attack_mitigation = true; web_attack_mitigation_action = + web_attack_mitigation_mode != "Prevent" ? "Transparent" : web_attack_mitigation_severity == "critical" ? "low" : web_attack_mitigation_severity == "high" ? "balanced" : web_attack_mitigation_severity == "medium" ? "high" : @@ -473,7 +479,7 @@ WebAppSection::WebAppSection( web_attack_mitigation_mode(_web_attack_mitigation_mode), practice_advanced_config(_practice_advanced_config), anti_bots(_anti_bots), - trusted_sources({parsed_trusted_sources}) + trusted_sources({ parsed_trusted_sources }) { web_attack_mitigation = true; web_attack_mitigation_action = @@ -488,6 +494,7 @@ WebAppSection::WebAppSection( overrides.push_back(AppSecOverride(source_ident)); } } + // LCOV_EXCL_STOP void @@ -525,7 +532,16 @@ WebAppSection::save(cereal::JSONOutputArchive &out_ar) const cereal::make_nvp("botProtection_v2", detect_str) ); } + // LCOV_EXCL_START Reason: no test exist + +bool +WebAppSection::operator<(const WebAppSection &other) const +{ + // for sorting from the most specific to the least specific rule + return application_urls.size() > other.application_urls.size(); +} + void WebAPISection::save(cereal::JSONOutputArchive &out_ar) const { @@ -554,8 +570,27 @@ WebAPISection::save(cereal::JSONOutputArchive &out_ar) const cereal::make_nvp("overrides", empty_list) ); } + +bool +WebAPISection::operator<(const WebAPISection &other) const +{ + // for sorting from the most specific to the least specific rule + return application_urls.size() > other.application_urls.size(); +} + // LCOV_EXCL_STOP +AppSecRulebase::AppSecRulebase( + std::vector _webApplicationPractices, + std::vector _webAPIPractices +) : + webApplicationPractices(_webApplicationPractices), + webAPIPractices(_webAPIPractices) +{ + sort(webAPIPractices.begin(), webAPIPractices.end()); + sort(webApplicationPractices.begin(), webApplicationPractices.end()); +} + void AppSecRulebase::save(cereal::JSONOutputArchive &out_ar) const { @@ -719,11 +754,7 @@ AppsecLinuxPolicy::serialize(cereal::JSONInputArchive &archive_in) parseAppsecJSONKey>("custom-responses", custom_responses, archive_in); parseAppsecJSONKey>("exceptions", exceptions, archive_in); parseAppsecJSONKey>("trusted-sources", trusted_sources, archive_in); - parseAppsecJSONKey>( - "source-identifiers", - sources_identifiers, - archive_in - ); + parseAppsecJSONKey>("source-identifiers", sources_identifiers, archive_in); } const AppsecPolicySpec & @@ -768,7 +799,6 @@ AppsecLinuxPolicy::getAppsecSourceIdentifierSpecs() const return sources_identifiers; } - const vector & AppsecLinuxPolicy::rpmGetRPSettings() const { diff --git a/components/security_apps/local_policy_mgmt_gen/exceptions_section.cc b/components/security_apps/local_policy_mgmt_gen/exceptions_section.cc index c60c4cd..d413895 100755 --- a/components/security_apps/local_policy_mgmt_gen/exceptions_section.cc +++ b/components/security_apps/local_policy_mgmt_gen/exceptions_section.cc @@ -241,11 +241,21 @@ ExceptionMatch::ExceptionMatch(const NewAppsecException &parsed_exception) items.push_back(ExceptionMatch("sourceIdentifier", parsed_exception.getSourceIdentifier())); } if (!parsed_exception.getSourceIp().empty()) { - items.push_back(ExceptionMatch("sourceIp", parsed_exception.getSourceIp())); + items.push_back(ExceptionMatch("sourceIP", parsed_exception.getSourceIp())); } if (!parsed_exception.getUrl().empty()) { items.push_back(ExceptionMatch("url", parsed_exception.getUrl())); } + + // when there is only one operand, there's no need for an additional 'and'/'or' condition enclosing it + if (items.size() == 1) { + auto & other = items[0]; + match_type = other.match_type; + op = other.op; + key = other.key; + value = other.value; + items = other.items; + } } void diff --git a/components/security_apps/local_policy_mgmt_gen/include/appsec_practice_section.h b/components/security_apps/local_policy_mgmt_gen/include/appsec_practice_section.h index eaf9d8d..be30a59 100644 --- a/components/security_apps/local_policy_mgmt_gen/include/appsec_practice_section.h +++ b/components/security_apps/local_policy_mgmt_gen/include/appsec_practice_section.h @@ -296,6 +296,8 @@ public: void save(cereal::JSONOutputArchive &out_ar) const; + bool operator< (const WebAppSection &other) const; + private: std::string application_urls; std::string asset_id; @@ -350,6 +352,8 @@ public: void save(cereal::JSONOutputArchive &out_ar) const; + bool operator< (const WebAPISection &other) const; + private: std::string application_urls; std::string asset_id; @@ -371,10 +375,7 @@ class AppSecRulebase public: AppSecRulebase( std::vector _webApplicationPractices, - std::vector _webAPIPractices) - : - webApplicationPractices(_webApplicationPractices), - webAPIPractices(_webAPIPractices) {} + std::vector _webAPIPractices); void save(cereal::JSONOutputArchive &out_ar) const; diff --git a/components/security_apps/local_policy_mgmt_gen/include/new_appsec_linux_policy.h b/components/security_apps/local_policy_mgmt_gen/include/new_appsec_linux_policy.h index cd664bd..b4fd890 100755 --- a/components/security_apps/local_policy_mgmt_gen/include/new_appsec_linux_policy.h +++ b/components/security_apps/local_policy_mgmt_gen/include/new_appsec_linux_policy.h @@ -32,7 +32,7 @@ #include "new_practice.h" #include "access_control_practice.h" #include "new_trusted_sources.h" - +#include "new_auto_upgrade.h" class V1beta2AppsecLinuxPolicy : Singleton::Consume { @@ -48,7 +48,8 @@ public: const std::vector &_custom_responses, const std::vector &_exceptions, const std::vector &_trusted_sources, - const std::vector &_sources_identifiers) + const std::vector &_sources_identifiers, + const AppSecAutoUpgradeSpec &_auto_upgrade) : policies(_policies), threat_prevection_practices(_threat_prevention_practices), @@ -57,7 +58,8 @@ public: custom_responses(_custom_responses), exceptions(_exceptions), trusted_sources(_trusted_sources), - sources_identifiers(_sources_identifiers) {} + sources_identifiers(_sources_identifiers), + auto_upgrade(_auto_upgrade) {} // LCOV_EXCL_STOP void serialize(cereal::JSONInputArchive &archive_in); @@ -69,6 +71,7 @@ public: const std::vector & getAppsecExceptions() const; const std::vector & getAppsecTrustedSourceSpecs() const; const std::vector & getAppsecSourceIdentifierSpecs() const; + const AppSecAutoUpgradeSpec & getAppSecAutoUpgradeSpec() const; void addSpecificRule(const NewParsedRule &_rule); private: @@ -80,6 +83,7 @@ private: std::vector exceptions; std::vector trusted_sources; std::vector sources_identifiers; + AppSecAutoUpgradeSpec auto_upgrade; }; #endif // __NEW_APPSEC_LINUX_POLICY_H__ diff --git a/components/security_apps/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 index db80891..6d868f9 100755 --- a/components/security_apps/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 @@ -42,6 +42,7 @@ public: const std::string & getSourceIdentifiers() const; const std::string & getCustomResponse() const; const std::string & getTrustedSources() const; + const std::string & getUpgradeSettings() const; const std::string & getHost() const; const std::string & getMode() const; @@ -56,6 +57,7 @@ private: std::string source_identifiers; std::string custom_response; std::string trusted_sources; + std::string upgrade_settings; std::string host; std::string mode; }; diff --git a/components/security_apps/local_policy_mgmt_gen/include/new_auto_upgrade.h b/components/security_apps/local_policy_mgmt_gen/include/new_auto_upgrade.h new file mode 100755 index 0000000..50a0021 --- /dev/null +++ b/components/security_apps/local_policy_mgmt_gen/include/new_auto_upgrade.h @@ -0,0 +1,47 @@ +// 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 __NEW_AUTO_UPGRADE_H__ +#define __NEW_AUTO_UPGRADE_H__ + +#include +#include +#include +#include +#include + +#include "config.h" +#include "debug.h" +#include "local_policy_common.h" + +class AppSecAutoUpgradeSpec +{ +public: + void load(cereal::JSONInputArchive &archive_in); + void save(cereal::JSONOutputArchive& out_ar) const; + + const std::string & getAppSecClassName() const; + const std::string & getName() const; + void setName(const std::string &_name); + +private: + std::string mode = "automatic"; + std::vector days; + std::string upgrade_window_start_hour_UTC; + uint upgrade_window_duration; + + std::string name; + std::string appsec_class_name; +}; + +#endif // __NEW_AUTO_UPGRADE_H__ diff --git a/components/security_apps/local_policy_mgmt_gen/include/new_log_trigger.h b/components/security_apps/local_policy_mgmt_gen/include/new_log_trigger.h index 8e36138..a39194a 100755 --- a/components/security_apps/local_policy_mgmt_gen/include/new_log_trigger.h +++ b/components/security_apps/local_policy_mgmt_gen/include/new_log_trigger.h @@ -30,9 +30,11 @@ class NewAppsecTriggerAccessControlLogging public: void load(cereal::JSONInputArchive &archive_in); + bool isAcAllowEvents() const { return ac_allow_events; } + bool isAcDropEvents() const { return ac_drop_events; } private: - bool allow_events = false; - bool drop_events = false; + bool ac_allow_events = false; + bool ac_drop_events = false; }; class NewAppsecTriggerAdditionalSuspiciousEventsLogging : public ClientRest @@ -158,6 +160,7 @@ public: const NewAppsecTriggerLogging & getAppsecTriggerLogging() const; const NewAppsecTriggerExtendedLogging & getAppsecTriggerExtendedLogging() const; const NewAppsecTriggerLogDestination & getAppsecTriggerLogDestination() const; + const NewAppsecTriggerAccessControlLogging & getAppsecTriggerAccessControlLogging() const; private: NewAppsecTriggerAccessControlLogging access_control_logging; diff --git a/components/security_apps/local_policy_mgmt_gen/include/new_practice.h b/components/security_apps/local_policy_mgmt_gen/include/new_practice.h index 58deb43..63658f7 100755 --- a/components/security_apps/local_policy_mgmt_gen/include/new_practice.h +++ b/components/security_apps/local_policy_mgmt_gen/include/new_practice.h @@ -481,17 +481,22 @@ private: class NewSnortSignaturesAndOpenSchemaAPI { public: + NewSnortSignaturesAndOpenSchemaAPI() : is_temporary(false) {}; + 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; + bool isTemporary() const; + void setTemporary(bool val); private: std::string override_mode; std::vector config_map; std::vector files; + bool is_temporary; }; class NewAppSecWebBotsURI diff --git a/components/security_apps/local_policy_mgmt_gen/include/policy_maker_utils.h b/components/security_apps/local_policy_mgmt_gen/include/policy_maker_utils.h old mode 100755 new mode 100644 index 8b9cedc..7a65e9d --- a/components/security_apps/local_policy_mgmt_gen/include/policy_maker_utils.h +++ b/components/security_apps/local_policy_mgmt_gen/include/policy_maker_utils.h @@ -51,6 +51,7 @@ enum class AnnotationTypes { WEB_USER_RES, SOURCE_IDENTIFIERS, TRUSTED_SOURCES, + UPGRADE_SETTINGS, COUNT }; @@ -96,16 +97,17 @@ class PolicyWrapper { public: PolicyWrapper( - const SettingsWrapper &_settings, + const SettingsRulebase &_settings, const SecurityAppsWrapper &_security_apps) : settings(_settings), security_apps(_security_apps) {} - void save(cereal::JSONOutputArchive &out_ar) const; + const SettingsRulebase & getSettings() const { return settings; } + const SecurityAppsWrapper & getSecurityApps() const { return security_apps; } private: - SettingsWrapper settings; + SettingsRulebase settings; SecurityAppsWrapper security_apps; }; @@ -139,7 +141,11 @@ private: std::tuple splitHostName(const std::string &host_name); - std::string dumpPolicyToFile(const PolicyWrapper &policy, const std::string &policy_path); + std::string dumpPolicyToFile( + const PolicyWrapper &policy, + const std::string &policy_path, + const std::string &settings_path = "/etc/cp/conf/settings.json" + ); PolicyWrapper combineElementsToPolicy(const std::string &policy_version); @@ -155,7 +161,7 @@ private: std::map &rule_annotations ); - void createSnortProtecionsSection(const std::string &file_name, const std::string &practic_name); + void createSnortProtecionsSection(const std::string &file_name, bool is_temporary); void createSnortSections( @@ -245,6 +251,7 @@ private: std::map rate_limit; std::map users_identifiers; std::map trusted_sources; + AppSecAutoUpgradeSpec upgrade_settings; }; template diff --git a/components/security_apps/local_policy_mgmt_gen/include/settings_section.h b/components/security_apps/local_policy_mgmt_gen/include/settings_section.h index ebbfaee..5d6c96e 100644 --- a/components/security_apps/local_policy_mgmt_gen/include/settings_section.h +++ b/components/security_apps/local_policy_mgmt_gen/include/settings_section.h @@ -22,6 +22,7 @@ #include "config.h" #include "debug.h" #include "local_policy_common.h" +#include "new_auto_upgrade.h" // LCOV_EXCL_START Reason: no test exist class AgentSettingsSection @@ -41,12 +42,18 @@ private: class SettingsRulebase { public: - SettingsRulebase(std::vector _agentSettings) : agentSettings(_agentSettings) {} + SettingsRulebase( + std::vector _agentSettings, + const AppSecAutoUpgradeSpec &_upgradeSettings) + : + agentSettings(_agentSettings), + upgrade_settings(_upgradeSettings) {} void save(cereal::JSONOutputArchive &out_ar) const; private: std::vector agentSettings; + AppSecAutoUpgradeSpec upgrade_settings; }; class SettingsWrapper diff --git a/components/security_apps/local_policy_mgmt_gen/include/triggers_section.h b/components/security_apps/local_policy_mgmt_gen/include/triggers_section.h index 5a76463..dc4998d 100644 --- a/components/security_apps/local_policy_mgmt_gen/include/triggers_section.h +++ b/components/security_apps/local_policy_mgmt_gen/include/triggers_section.h @@ -44,6 +44,8 @@ public: bool _responseBody, bool _tpDetect, bool _tpPrevent, + bool _acAllow, + bool _acDrop, bool _webBody, bool _webHeaders, bool _webRequests, @@ -76,6 +78,8 @@ private: bool responseBody; bool tpDetect; bool tpPrevent; + bool acAllow; + bool acDrop; bool webBody; bool webHeaders; bool webRequests; @@ -158,9 +162,11 @@ class AppsecTriggerAccessControlLogging public: void load(cereal::JSONInputArchive &archive_in); + bool isAcAllowEvents() const { return ac_allow_events; } + bool isAcDropEvents() const { return ac_drop_events; } private: - bool allow_events = false; - bool drop_events = false; + bool ac_allow_events = false; + bool ac_drop_events = false; }; class AppsecTriggerAdditionalSuspiciousEventsLogging : public ClientRest @@ -281,6 +287,7 @@ public: const AppsecTriggerLogging & getAppsecTriggerLogging() const; const AppsecTriggerExtendedLogging & getAppsecTriggerExtendedLogging() const; const AppsecTriggerLogDestination & getAppsecTriggerLogDestination() const; + const AppsecTriggerAccessControlLogging & getAppsecTriggerAccessControlLogging() const; private: AppsecTriggerAccessControlLogging access_control_logging; diff --git a/components/security_apps/local_policy_mgmt_gen/k8s_policy_utils.cc b/components/security_apps/local_policy_mgmt_gen/k8s_policy_utils.cc index 976ae85..a036d17 100644 --- a/components/security_apps/local_policy_mgmt_gen/k8s_policy_utils.cc +++ b/components/security_apps/local_policy_mgmt_gen/k8s_policy_utils.cc @@ -159,6 +159,7 @@ extractElementsFromNewRule( policy_elements_names[AnnotationTypes::WEB_USER_RES].insert(rule.getCustomResponse()); policy_elements_names[AnnotationTypes::SOURCE_IDENTIFIERS].insert(rule.getSourceIdentifiers()); policy_elements_names[AnnotationTypes::TRUSTED_SOURCES].insert(rule.getTrustedSources()); + policy_elements_names[AnnotationTypes::UPGRADE_SETTINGS].insert(rule.getUpgradeSettings()); } map> @@ -356,8 +357,9 @@ 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"; + auto path = getFilesystemPathConfig() + "/conf/snort/snort_k8s_" + practice.getName() + ".rule"; bool append_mode = false; + practice.getSnortSignatures().setTemporary(true); for (const string &config_map : practice.getSnortSignatures().getConfigMap()) { auto maybe_configmap = getObjectFromCluster( @@ -441,6 +443,15 @@ K8sPolicyUtils::createAppsecPolicyK8sFromV1beta2Crds( policy_elements_names[AnnotationTypes::TRUSTED_SOURCES] ); + vector vec_upgrade_settings = extractV1Beta2ElementsFromCluster( + "autoupgrade", + policy_elements_names[AnnotationTypes::UPGRADE_SETTINGS] + ); + if (vec_upgrade_settings.size() > 1) { + dbgWarning(D_LOCAL_POLICY) << "Only one definition of upgrade settings is required."; + } + auto upgrade_settings = vec_upgrade_settings.empty() ? AppSecAutoUpgradeSpec() : vec_upgrade_settings.front(); + V1beta2AppsecLinuxPolicy appsec_policy = V1beta2AppsecLinuxPolicy( appsec_policy_spec.getSpec(), threat_prevention_practices, @@ -449,7 +460,8 @@ K8sPolicyUtils::createAppsecPolicyK8sFromV1beta2Crds( web_user_responses, exceptions, trusted_sources, - source_identifiers + source_identifiers, + upgrade_settings ); return appsec_policy; } diff --git a/components/security_apps/local_policy_mgmt_gen/new_appsec_linux_policy.cc b/components/security_apps/local_policy_mgmt_gen/new_appsec_linux_policy.cc index c2a77e1..25fa7e5 100755 --- a/components/security_apps/local_policy_mgmt_gen/new_appsec_linux_policy.cc +++ b/components/security_apps/local_policy_mgmt_gen/new_appsec_linux_policy.cc @@ -64,6 +64,12 @@ V1beta2AppsecLinuxPolicy::getAppsecSourceIdentifierSpecs() const return sources_identifiers; } +const AppSecAutoUpgradeSpec & +V1beta2AppsecLinuxPolicy::getAppSecAutoUpgradeSpec() const +{ + return auto_upgrade; +} + void V1beta2AppsecLinuxPolicy::addSpecificRule(const NewParsedRule &_rule) { @@ -97,4 +103,5 @@ V1beta2AppsecLinuxPolicy::serialize(cereal::JSONInputArchive &archive_in) parseAppsecJSONKey>("exceptions", exceptions, archive_in); parseAppsecJSONKey>("trustedSources", trusted_sources, archive_in); parseAppsecJSONKey>("sourcesIdentifiers", sources_identifiers, archive_in); + parseAppsecJSONKey("autoUpgrade", auto_upgrade, archive_in); } diff --git a/components/security_apps/local_policy_mgmt_gen/new_appsec_policy_crd_parser.cc b/components/security_apps/local_policy_mgmt_gen/new_appsec_policy_crd_parser.cc index 4fdc0ef..42b1151 100755 --- a/components/security_apps/local_policy_mgmt_gen/new_appsec_policy_crd_parser.cc +++ b/components/security_apps/local_policy_mgmt_gen/new_appsec_policy_crd_parser.cc @@ -35,6 +35,7 @@ NewParsedRule::load(cereal::JSONInputArchive &archive_in) parseAppsecJSONKey("customResponse", custom_response, archive_in); parseAppsecJSONKey("sourceIdentifiers", source_identifiers, archive_in); parseAppsecJSONKey("trustedSources", trusted_sources, archive_in); + parseAppsecJSONKey("autoUpgrade", upgrade_settings, archive_in); try { archive_in(cereal::make_nvp("host", host)); } catch (const cereal::Exception &e) @@ -86,6 +87,12 @@ NewParsedRule::getTrustedSources() const return trusted_sources; } +const string & +NewParsedRule::getUpgradeSettings() const +{ + return upgrade_settings; +} + const string & NewParsedRule::getHost() const { diff --git a/components/security_apps/local_policy_mgmt_gen/new_auto_upgrade.cc b/components/security_apps/local_policy_mgmt_gen/new_auto_upgrade.cc new file mode 100755 index 0000000..ddf51ae --- /dev/null +++ b/components/security_apps/local_policy_mgmt_gen/new_auto_upgrade.cc @@ -0,0 +1,118 @@ +// 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 "new_auto_upgrade.h" + +using namespace std; + +USE_DEBUG_FLAG(D_LOCAL_POLICY); + +static const set valid_modes = {"automatic", "manual", "scheduled"}; +static const set valid_days_of_week = { + "monday", + "tuesday", + "wednesday", + "thursday", + "friday", + "saturday", + "sunday" +}; + +class AppSecScheduledUpgrade +{ +public: + void + load(cereal::JSONInputArchive &archive_in) + { + parseAppsecJSONKey>("days", days, archive_in); + for (const string &day : days) { + if (valid_days_of_week.count(day) == 0) { + dbgWarning(D_LOCAL_POLICY) << "AppSec upgrade day invalid: " << day; + } + } + parseAppsecJSONKey("upgradeWindowStartHourUTC", upgrade_window_start_hour_UTC, archive_in, "0:00"); + parseAppsecJSONKey("upgradeWindowDuration", upgrade_window_duration, archive_in, 4); + } + + const vector & + getDays() const + { + return days; + } + + const string & + getUpgradeWindowStartHourUTC() const + { + return upgrade_window_start_hour_UTC; + } + + const uint & + getUpgradeWindowDuration() const + { + return upgrade_window_duration; + } + +private: + vector days; + string upgrade_window_start_hour_UTC = "0:00"; + uint upgrade_window_duration = 4; +}; + +void +AppSecAutoUpgradeSpec::load(cereal::JSONInputArchive &archive_in) +{ + dbgTrace(D_LOCAL_POLICY) << "Loading AppSec upgrade settings spec"; + parseAppsecJSONKey("appsecClassName", appsec_class_name, archive_in); + parseAppsecJSONKey("name", name, archive_in); + parseAppsecJSONKey("mode", mode, archive_in); + if (valid_modes.count(mode) == 0) { + dbgWarning(D_LOCAL_POLICY) << "AppSec upgrade mode invalid: " << mode; + } + if (mode != "scheduled") return; + + AppSecScheduledUpgrade schedule; + parseAppsecJSONKey("schedule", schedule, archive_in); + days = schedule.getDays(); + upgrade_window_start_hour_UTC = schedule.getUpgradeWindowStartHourUTC(); + upgrade_window_duration = schedule.getUpgradeWindowDuration(); +} + +void +AppSecAutoUpgradeSpec::save(cereal::JSONOutputArchive& out_ar) const +{ + out_ar(cereal::make_nvp("upgradeMode", mode)); + if (mode != "scheduled") return; + out_ar( + cereal::make_nvp("upgradeTime", upgrade_window_start_hour_UTC), + cereal::make_nvp("upgradeDurationHours", upgrade_window_duration), + cereal::make_nvp("upgradeDay", days) + ); +} + +void +AppSecAutoUpgradeSpec::setName(const string &_name) +{ + name = _name; +} + +const string & +AppSecAutoUpgradeSpec::getName() const +{ + return name; +} + +const string & +AppSecAutoUpgradeSpec::getAppSecClassName() const +{ + return appsec_class_name; +} diff --git a/components/security_apps/local_policy_mgmt_gen/new_exceptions.cc b/components/security_apps/local_policy_mgmt_gen/new_exceptions.cc index 2d6052b..b3b19a4 100755 --- a/components/security_apps/local_policy_mgmt_gen/new_exceptions.cc +++ b/components/security_apps/local_policy_mgmt_gen/new_exceptions.cc @@ -23,9 +23,9 @@ static const set valid_actions = {"skip", "accept", "drop", "suppressLog void NewAppsecExceptionCondition::load(cereal::JSONInputArchive &archive_in) { - dbgTrace(D_LOCAL_POLICY) << "Loading New AppSec exception condition"; parseAppsecJSONKey("key", key, archive_in); parseAppsecJSONKey("value", value, archive_in); + dbgTrace(D_LOCAL_POLICY) << "Key: " << key << " Value: " << value; } const string & diff --git a/components/security_apps/local_policy_mgmt_gen/new_log_trigger.cc b/components/security_apps/local_policy_mgmt_gen/new_log_trigger.cc index 13dded3..1e1dc8d 100755 --- a/components/security_apps/local_policy_mgmt_gen/new_log_trigger.cc +++ b/components/security_apps/local_policy_mgmt_gen/new_log_trigger.cc @@ -26,8 +26,8 @@ void NewAppsecTriggerAccessControlLogging::load(cereal::JSONInputArchive &archive_in) { dbgTrace(D_LOCAL_POLICY) << "Loading AppSec Trigger - Access Control Logging"; - parseAppsecJSONKey("allowEvents", allow_events, archive_in, false); - parseAppsecJSONKey("dropEvents", drop_events, archive_in, false); + parseAppsecJSONKey("allowEvents", ac_allow_events, archive_in, false); + parseAppsecJSONKey("dropEvents", ac_drop_events, archive_in, false); } void @@ -307,6 +307,13 @@ NewAppsecLogTrigger::getAppsecTriggerLogging() const return appsec_logging; } +const NewAppsecTriggerAccessControlLogging & +NewAppsecLogTrigger::getAppsecTriggerAccessControlLogging() const +{ + return access_control_logging; +} + + const NewAppsecTriggerExtendedLogging & NewAppsecLogTrigger::getAppsecTriggerExtendedLogging() const { diff --git a/components/security_apps/local_policy_mgmt_gen/new_practice.cc b/components/security_apps/local_policy_mgmt_gen/new_practice.cc index 9d1cf67..883dd16 100755 --- a/components/security_apps/local_policy_mgmt_gen/new_practice.cc +++ b/components/security_apps/local_policy_mgmt_gen/new_practice.cc @@ -107,9 +107,9 @@ void NewAppSecWebAttackProtections::load(cereal::JSONInputArchive &archive_in) { dbgTrace(D_LOCAL_POLICY) << "Loading AppSec Web Attack Protections"; - parseAppsecJSONKey("csrfEnabled", csrf_protection, archive_in, "inactive"); - parseAppsecJSONKey("errorDisclosureEnabled", error_disclosure, archive_in, "inactive"); - parseAppsecJSONKey("openRedirectEnabled", open_redirect, archive_in, "inactive"); + parseAppsecJSONKey("csrfProtection", csrf_protection, archive_in, "inactive"); + parseAppsecJSONKey("errorDisclosure", error_disclosure, archive_in, "inactive"); + parseAppsecJSONKey("openRedirect", open_redirect, archive_in, "inactive"); parseAppsecJSONKey("nonValidHttpMethods", non_valid_http_methods, archive_in, false); } @@ -441,6 +441,8 @@ NewSnortSignaturesAndOpenSchemaAPI::load(cereal::JSONInputArchive &archive_in) dbgTrace(D_LOCAL_POLICY) << "Loading AppSec Snort Signatures practice"; parseAppsecJSONKey("overrideMode", override_mode, archive_in, "inactive"); parseAppsecJSONKey>("configmap", config_map, archive_in); + parseAppsecJSONKey>("files", files, archive_in); + is_temporary = false; if (valid_modes.count(override_mode) == 0) { dbgWarning(D_LOCAL_POLICY) << "AppSec Snort Signatures override mode invalid: " << override_mode; } @@ -470,6 +472,18 @@ NewSnortSignaturesAndOpenSchemaAPI::getConfigMap() const return config_map; } +bool +NewSnortSignaturesAndOpenSchemaAPI::isTemporary() const +{ + return is_temporary; +} + +void +NewSnortSignaturesAndOpenSchemaAPI::setTemporary(bool val) +{ + is_temporary = val; +} + void IpsProtectionsRulesSection::save(cereal::JSONOutputArchive &out_ar) const { diff --git a/components/security_apps/local_policy_mgmt_gen/policy_maker_utils.cc b/components/security_apps/local_policy_mgmt_gen/policy_maker_utils.cc index 7f8c5a2..cd64315 100755 --- a/components/security_apps/local_policy_mgmt_gen/policy_maker_utils.cc +++ b/components/security_apps/local_policy_mgmt_gen/policy_maker_utils.cc @@ -38,12 +38,6 @@ SecurityAppsWrapper::save(cereal::JSONOutputArchive &out_ar) const ); } -void -PolicyWrapper::save(cereal::JSONOutputArchive &out_ar) const -{ - security_apps.save(out_ar); -} - string PolicyMakerUtils::getPolicyName(const string &policy_path) { @@ -150,16 +144,19 @@ PolicyMakerUtils::splitHostName(const string &host_name) } string -PolicyMakerUtils::dumpPolicyToFile(const PolicyWrapper &policy, const string &policy_path) +PolicyMakerUtils::dumpPolicyToFile( + const PolicyWrapper &policy, + const string &policy_path, + const string &settings_path) { clearElementsMaps(); - stringstream ss; + stringstream policy_ss, settings_ss; { - cereal::JSONOutputArchive ar(ss); - policy.save(ar); + cereal::JSONOutputArchive ar(policy_ss); + policy.getSecurityApps().save(ar); } - string policy_str = ss.str(); + string policy_str = policy_ss.str(); try { ofstream policy_file(policy_path); policy_file << policy_str; @@ -169,6 +166,20 @@ PolicyMakerUtils::dumpPolicyToFile(const PolicyWrapper &policy, const string &po return ""; } + { + cereal::JSONOutputArchive ar(settings_ss); + policy.getSettings().save(ar); + } + string settings_str = settings_ss.str(); + try { + ofstream settings_file(settings_path); + settings_file << settings_str; + settings_file.close(); + } catch (const ofstream::failure &e) { + dbgDebug(D_NGINX_POLICY) << "Error while writing settings to " << settings_path << ", Error: " << e.what(); + } + dbgDebug(D_LOCAL_POLICY) << settings_path << " content: " << settings_str; + return policy_str; } @@ -517,6 +528,8 @@ extractLogTriggerData(const string &trigger_annotation_name, const T &trigger_sp trigger_spec.getAppsecTriggerAdditionalSuspiciousEventsLogging().getMinimumSeverity(); bool tpDetect = trigger_spec.getAppsecTriggerLogging().isDetectEvents(); bool tpPrevent = trigger_spec.getAppsecTriggerLogging().isPreventEvents(); + bool acAllow = trigger_spec.getAppsecTriggerAccessControlLogging().isAcAllowEvents(); + bool acDrop = trigger_spec.getAppsecTriggerAccessControlLogging().isAcDropEvents(); bool webRequests = trigger_spec.getAppsecTriggerLogging().isAllWebRequests(); bool webUrlPath = trigger_spec.getAppsecTriggerExtendedLogging().isUrlPath(); bool webUrlQuery = trigger_spec.getAppsecTriggerExtendedLogging().isUrlQuery(); @@ -555,6 +568,8 @@ extractLogTriggerData(const string &trigger_annotation_name, const T &trigger_sp responseBody, tpDetect, tpPrevent, + acAllow, + acDrop, webBody, webHeaders, webRequests, @@ -1004,13 +1019,21 @@ PolicyMakerUtils::createIpsSections( } void -PolicyMakerUtils::createSnortProtecionsSection(const string &file_name, const string &practice_name) +PolicyMakerUtils::createSnortProtecionsSection(const string &file_name, bool is_temporary) { - auto path = getFilesystemPathConfig() + "/conf/snort/snort_k8s_" + practice_name; - if (snort_protections.find(path) != snort_protections.end()) return; + auto path = getFilesystemPathConfig() + "/conf/snort/" + file_name; + string in_file = is_temporary ? path + ".rule" : path; - auto snort_scriipt_path = getFilesystemPathConfig() + "/scripts/snort_to_ips_local.py"; - auto cmd = "python " + snort_scriipt_path + " " + path + ".rule " + path + ".out " + path + ".err"; + if (snort_protections.find(path) != snort_protections.end()) { + dbgTrace(D_LOCAL_POLICY) << "Snort protections section for file " << file_name << " already exists"; + return; + } + dbgTrace(D_LOCAL_POLICY) + << "Reading snort signatures from" + << (is_temporary ? " temporary" : "") << " file " << path; + + auto snort_script_path = getFilesystemPathConfig() + "/scripts/snort_to_ips_local.py"; + auto cmd = "python3 " + snort_script_path + " " + in_file + " " + path + ".out " + path + ".err"; auto res = Singleton::Consume::by()->getExecOutput(cmd); @@ -1026,7 +1049,7 @@ PolicyMakerUtils::createSnortProtecionsSection(const string &file_name, const st } auto i_orchestration_tools = Singleton::Consume::by(); - i_orchestration_tools->removeFile(path + ".rule"); + if (is_temporary) i_orchestration_tools->removeFile(in_file); i_orchestration_tools->removeFile(path + ".out"); i_orchestration_tools->removeFile(path + ".err"); @@ -1055,7 +1078,14 @@ PolicyMakerUtils::createSnortSections( apssec_practice.getSnortSignatures().getFiles().size() == 0) { return; } - createSnortProtecionsSection(apssec_practice.getSnortSignatures().getFiles()[0], apssec_practice.getName()); + + if (apssec_practice.getSnortSignatures().isTemporary()) { + createSnortProtecionsSection("snort_k8s_" + apssec_practice.getName(), true); + } else if (apssec_practice.getSnortSignatures().getFiles().size() > 0) { + // when support for multiple files is ready, will iterate over the files array + auto file = apssec_practice.getSnortSignatures().getFiles()[0]; + createSnortProtecionsSection(file, false); + } SnortProtectionsSection snort_section = SnortProtectionsSection( context, @@ -1160,7 +1190,7 @@ PolicyMakerUtils::createWebAppSection( apssec_practice.getWebAttacks().getMaxUrlSizeBytes() ); WebAppSection web_app = WebAppSection( - full_url == "Any" ? "" : full_url, + full_url == "Any" ? "http://*:*" : full_url, rule_config.getAssetId(), rule_config.getAssetName(), rule_config.getAssetId(), @@ -1271,17 +1301,16 @@ PolicyMakerUtils::createThreatPreventionPracticeSections( } -SettingsWrapper -createProfilesSection() +SettingsRulebase +createSettingsSection(const AppSecAutoUpgradeSpec &upgrade_settings) { string agent_settings_key = "agent.test.policy"; string agent_settings_value = "local policy"; AgentSettingsSection agent_setting_1 = AgentSettingsSection(agent_settings_key, agent_settings_value); - SettingsRulebase settings_rulebase_1 = SettingsRulebase({agent_setting_1}); - return SettingsWrapper(settings_rulebase_1); + return SettingsRulebase({agent_setting_1}, upgrade_settings); + } -// LCOV_EXCL_STOP PolicyWrapper PolicyMakerUtils::combineElementsToPolicy(const string &policy_version) @@ -1313,8 +1342,8 @@ PolicyMakerUtils::combineElementsToPolicy(const string &policy_version) policy_version ); - SettingsWrapper profiles_section = createProfilesSection(); - PolicyWrapper policy_wrapper = PolicyWrapper(profiles_section, security_app_section); + SettingsRulebase settings_section = createSettingsSection(upgrade_settings); + PolicyWrapper policy_wrapper = PolicyWrapper(settings_section, security_app_section); return policy_wrapper; } @@ -1433,7 +1462,7 @@ PolicyMakerUtils::createPolicyElementsByRule( if (!web_apps.count(rule_config.getAssetName())) { WebAppSection web_app = WebAppSection( - full_url == "Any" ? "" : full_url, + full_url == "Any" ? "http://*:*" : full_url, rule_config.getAssetId(), rule_config.getAssetName(), rule_config.getAssetId(), @@ -1553,6 +1582,7 @@ PolicyMakerUtils::createPolicyElementsByRule("allow-events", allow_events, archive_in, false); - parseAppsecJSONKey("drop-events", drop_events, archive_in, false); + parseAppsecJSONKey("allow-events", ac_allow_events, archive_in, false); + parseAppsecJSONKey("drop-events", ac_drop_events, archive_in, false); } void @@ -526,6 +530,13 @@ AppsecTriggerSpec::getAppsecTriggerLogDestination() const return log_destination; } +const AppsecTriggerAccessControlLogging & +AppsecTriggerSpec::getAppsecTriggerAccessControlLogging() const +{ + return access_control_logging; +} + + void TriggersWrapper::save(cereal::JSONOutputArchive &out_ar) const { diff --git a/components/security_apps/orchestration/include/declarative_policy_utils.h b/components/security_apps/orchestration/include/declarative_policy_utils.h index 58eb01e..1db30ce 100644 --- a/components/security_apps/orchestration/include/declarative_policy_utils.h +++ b/components/security_apps/orchestration/include/declarative_policy_utils.h @@ -14,7 +14,6 @@ #include "i_orchestration_tools.h" #include "i_agent_details.h" #include "i_orchestration_status.h" -#include "i_messaging.h" #include "i_mainloop.h" #include "i_encryptor.h" #include "i_details_resolver.h" @@ -23,6 +22,7 @@ #include "i_shell_cmd.h" #include "i_encryptor.h" #include "i_env_details.h" +#include "i_declarative_policy.h" #include "maybe_res.h" #include "event.h" #include "rest.h" @@ -43,6 +43,7 @@ private: class DeclarativePolicyUtils : + public Singleton::Provide::SelfInterface, public Singleton::Consume, Singleton::Consume, Singleton::Consume, @@ -75,13 +76,12 @@ public: const std::string &tenant_id, const std::string &profile_id, const std::string &fog_address - ); - std::string getUpdate(CheckUpdateRequest &request); - bool shouldApplyPolicy(); - void turnOffApplyPolicyFlag(); + ) override; + std::string getUpdate(CheckUpdateRequest &request) override; + bool shouldApplyPolicy() override; + void turnOffApplyPolicyFlag() override; - std::string getCurrVersion() { return curr_version; } - std::string getCurrPolicy() { return curr_policy; } + std::string getCurrPolicy() override { return curr_policy; } void upon(const ApplyPolicyEvent &event) override; diff --git a/components/security_apps/orchestration/include/fog_communication.h b/components/security_apps/orchestration/include/fog_communication.h index 3c27003..8406077 100755 --- a/components/security_apps/orchestration/include/fog_communication.h +++ b/components/security_apps/orchestration/include/fog_communication.h @@ -47,7 +47,7 @@ public: ) const override; private: - DeclarativePolicyUtils declarative_policy_utils; + I_DeclarativePolicy *i_declarative_policy = nullptr; }; #endif // __FOG_COMMUNICATION_H__ diff --git a/components/security_apps/orchestration/include/hybrid_communication.h b/components/security_apps/orchestration/include/hybrid_communication.h index 84648fc..b74e1ae 100755 --- a/components/security_apps/orchestration/include/hybrid_communication.h +++ b/components/security_apps/orchestration/include/hybrid_communication.h @@ -54,7 +54,7 @@ public: private: Maybe getNewVersion(); - DeclarativePolicyUtils declarative_policy_utils; + I_DeclarativePolicy *i_declarative_policy = nullptr; }; #endif // __HYBRID_COMMUNICATION_H__ diff --git a/components/security_apps/orchestration/include/i_declarative_policy.h b/components/security_apps/orchestration/include/i_declarative_policy.h new file mode 100644 index 0000000..1390766 --- /dev/null +++ b/components/security_apps/orchestration/include/i_declarative_policy.h @@ -0,0 +1,32 @@ +#ifndef __I_DECLARATIVE_POLICY__ +#define __I_DECLARATIVE_POLICY__ + +#include + +#include "singleton.h" +#include "orchestrator/rest_api/orchestration_check_update.h" + +class I_DeclarativePolicy +{ +public: + virtual bool shouldApplyPolicy() = 0; + + virtual std::string getUpdate(CheckUpdateRequest &request) = 0; + + virtual void sendUpdatesToFog( + const std::string &access_token, + const std::string &tenant_id, + const std::string &profile_id, + const std::string &fog_address + ) = 0; + + virtual std::string getCurrPolicy() = 0; + + virtual void turnOffApplyPolicyFlag() = 0; + +protected: + virtual ~I_DeclarativePolicy() {} +}; + + +#endif // __I_DECLARATIVE_POLICY__ diff --git a/components/security_apps/orchestration/include/mock/mock_update_communication.h b/components/security_apps/orchestration/include/mock/mock_update_communication.h index 12960f4..c5e9649 100755 --- a/components/security_apps/orchestration/include/mock/mock_update_communication.h +++ b/components/security_apps/orchestration/include/mock/mock_update_communication.h @@ -27,9 +27,13 @@ class MockUpdateCommunication : public Singleton::Provide::From> { public: + void init() {} MOCK_METHOD0(authenticateAgent, Maybe()); MOCK_METHOD1(getUpdate, Maybe(CheckUpdateRequest &)); - MOCK_METHOD1(downloadAttributeFile, Maybe(const GetResourceFile &)); + MOCK_METHOD2( + downloadAttributeFile, + Maybe(const GetResourceFile &, const std::string &) + ); MOCK_METHOD1(setAddressExtenesion, void(const std::string &)); MOCK_CONST_METHOD2(sendPolicyVersion, Maybe(const std::string &, const std::string &)); }; diff --git a/components/security_apps/orchestration/orchestration_comp.cc b/components/security_apps/orchestration/orchestration_comp.cc index 710103c..cb06c6c 100755 --- a/components/security_apps/orchestration/orchestration_comp.cc +++ b/components/security_apps/orchestration/orchestration_comp.cc @@ -1668,6 +1668,7 @@ private: if (getAttribute("no-setting", "CROWDSEC_ENABLED") == "true") tags.insert(Tags::CROWDSEC); if (getAttribute("no-setting", "PLAYGROUND") == "true") tags.insert(Tags::PLAYGROUND); + if (getAttribute("no-setting", "nginxproxymanager") == "true") tags.insert(Tags::NGINX_PROXY_MANAGER); Report registration_report( "Local Agent Data", diff --git a/components/security_apps/orchestration/service_controller/service_controller.cc b/components/security_apps/orchestration/service_controller/service_controller.cc index 6f7e925..7c73690 100755 --- a/components/security_apps/orchestration/service_controller/service_controller.cc +++ b/components/security_apps/orchestration/service_controller/service_controller.cc @@ -27,6 +27,7 @@ #include "log_generator.h" #include "i_orchestration_tools.h" #include "customized_cereal_map.h" +#include "declarative_policy_utils.h" using namespace std; using namespace ReportIS; @@ -745,6 +746,7 @@ ServiceController::Impl::updateServiceConfiguration( dbgDebug(D_ORCHESTRATOR) << "Policy file was not updated. Sending reload command regarding settings and data"; auto signal_services = sendSignalForServices(nano_services_to_update, ""); if (!signal_services.ok()) return signal_services.passErr(); + Singleton::Consume::from()->turnOffApplyPolicyFlag(); return Maybe(); } @@ -888,6 +890,7 @@ ServiceController::Impl::updateServiceConfiguration( if (new_policy_path.compare(config_file_path) == 0) { dbgDebug(D_ORCHESTRATOR) << "Enforcing the default policy file"; policy_version = version_value; + Singleton::Consume::from()->turnOffApplyPolicyFlag(); return Maybe(); } @@ -906,6 +909,7 @@ ServiceController::Impl::updateServiceConfiguration( } if (!was_policy_updated && !send_signal_for_services_err.empty()) return genError(send_signal_for_services_err); + Singleton::Consume::from()->turnOffApplyPolicyFlag(); return Maybe(); } 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 c9c6693..d027b0c 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 @@ -7,6 +7,7 @@ #include "service_controller.h" #include "config.h" #include "config_component.h" +#include "declarative_policy_utils.h" #include "mock/mock_orchestration_tools.h" #include "mock/mock_orchestration_status.h" #include "mock/mock_time_get.h" @@ -158,10 +159,26 @@ public: return string_stream.str(); } + void + expectNewConfigRequest(const string &req_body, const string &response) + { + EXPECT_CALL( + mock_message, + sendSyncMessage( + HTTPMethod::POST, + "/set-new-configuration", + req_body, + _, + _ + ) + ).WillOnce(Return(HTTPResponse(HTTPStatusCode::HTTP_OK, response))); + } + const uint16_t l4_firewall_service_port = 8888; const uint16_t waap_service_port = 7777; ::Environment env; ConfigComponent config; + DeclarativePolicyUtils declarative_policy_utils; string configuration_dir; string policy_extension; string settings_extension; @@ -176,7 +193,7 @@ public: string services_port; StrictMock time; StrictMock mock_rest_api; - StrictMock mock_message; + StrictMock mock_message; StrictMock mock_ml; StrictMock mock_shell_cmd; StrictMock mock_orchestration_status; @@ -254,6 +271,9 @@ TEST_F(ServiceControllerTest, UpdateConfiguration) EXPECT_EQ(i_service_controller->getPolicyVersion(), ""); EXPECT_EQ(i_service_controller->getPolicyVersions(), ""); + EXPECT_CALL(mock_orchestration_tools, calculateChecksum(Package::ChecksumTypes::MD5, file_name)) + .WillOnce(Return(version_value)); + 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)); @@ -262,23 +282,7 @@ TEST_F(ServiceControllerTest, UpdateConfiguration) string general_settings_path = "/my/settings/path"; string reply_msg = "{\"id\": 1, \"error\": false, \"finished\": true, \"error_message\": \"\"}"; - Flags conn_flags; - conn_flags.setFlag(MessageConnConfig::ONE_TIME_CONN); - EXPECT_CALL( - mock_message, - sendMessage( - true, - "{\n \"id\": 1,\n \"policy_version\": \"1.0.2\"\n}", - I_Messaging::Method::POST, - string("127.0.0.1"), - l4_firewall_service_port, - conn_flags, - string("/set-new-configuration"), - string(), - _, - MessageTypeTag::GENERIC - ) - ).WillOnce(Return(Maybe(reply_msg))); + expectNewConfigRequest("{\n \"id\": 1,\n \"policy_version\": \"1.0.2,1.0.2\"\n}", reply_msg); EXPECT_CALL( mock_shell_cmd, @@ -369,6 +373,9 @@ TEST_F(ServiceControllerTest, supportVersions) EXPECT_EQ(i_service_controller->getPolicyVersion(), ""); EXPECT_EQ(i_service_controller->getPolicyVersions(), ""); + EXPECT_CALL(mock_orchestration_tools, calculateChecksum(Package::ChecksumTypes::MD5, file_name)) + .WillOnce(Return(version_value)); + 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)); @@ -377,23 +384,7 @@ TEST_F(ServiceControllerTest, supportVersions) string general_settings_path = "/my/settings/path"; string reply_msg = "{\"id\": 1, \"error\": false, \"finished\": true, \"error_message\": \"\"}"; - Flags conn_flags; - conn_flags.setFlag(MessageConnConfig::ONE_TIME_CONN); - EXPECT_CALL( - mock_message, - sendMessage( - true, - "{\n \"id\": 1,\n \"policy_version\": \"1.0.2\"\n}", - I_Messaging::Method::POST, - string("127.0.0.1"), - l4_firewall_service_port, - conn_flags, - string("/set-new-configuration"), - string(), - _, - MessageTypeTag::GENERIC - ) - ).WillOnce(Return(Maybe(reply_msg))); + expectNewConfigRequest("{\n \"id\": 1,\n \"policy_version\": \"1.0.2,1.0.2\"\n}", reply_msg); EXPECT_CALL( mock_shell_cmd, @@ -464,6 +455,9 @@ TEST_F(ServiceControllerTest, TimeOutUpdateConfiguration) EXPECT_EQ(i_service_controller->getPolicyVersion(), ""); + EXPECT_CALL(mock_orchestration_tools, calculateChecksum(Package::ChecksumTypes::MD5, file_name)) + .WillOnce(Return(version_value)); + 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)); @@ -493,24 +487,7 @@ TEST_F(ServiceControllerTest, TimeOutUpdateConfiguration) string general_settings_path = "/my/settings/path"; string reply_msg = "{\"id\": 1, \"error\": false, \"finished\": true, \"error_message\": \"\"}"; - - Flags conn_flags; - conn_flags.setFlag(MessageConnConfig::ONE_TIME_CONN); - EXPECT_CALL( - mock_message, - sendMessage( - true, - "{\n \"id\": 1,\n \"policy_version\": \"1.0.2\"\n}", - I_Messaging::Method::POST, - string("127.0.0.1"), - l4_firewall_service_port, - conn_flags, - string("/set-new-configuration"), - string(), - _, - MessageTypeTag::GENERIC - ) - ).WillOnce(Return(Maybe(reply_msg))); + expectNewConfigRequest("{\n \"id\": 1,\n \"policy_version\": \"1.0.2,1.0.2\"\n}", reply_msg); EXPECT_TRUE(i_service_controller->updateServiceConfiguration(file_name, general_settings_path).ok()); EXPECT_EQ(i_service_controller->getPolicyVersion(), version_value); @@ -585,6 +562,9 @@ TEST_F(ServiceControllerTest, writeRegisteredServicesFromFile) EXPECT_EQ(i_service_controller->getPolicyVersion(), ""); + EXPECT_CALL(mock_orchestration_tools, calculateChecksum(Package::ChecksumTypes::MD5, file_name)) + .WillOnce(Return(version_value)); + 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)); @@ -593,23 +573,7 @@ TEST_F(ServiceControllerTest, writeRegisteredServicesFromFile) string general_settings_path = "/my/settings/path"; string reply_msg = "{\"id\": 1, \"error\": false, \"finished\": true, \"error_message\": \"\"}"; - Flags conn_flags; - conn_flags.setFlag(MessageConnConfig::ONE_TIME_CONN); - EXPECT_CALL( - mock_message, - sendMessage( - true, - "{\n \"id\": 1,\n \"policy_version\": \"1.0.2\"\n}", - I_Messaging::Method::POST, - string("127.0.0.1"), - l4_firewall_service_port, - conn_flags, - string("/set-new-configuration"), - string(), - _, - MessageTypeTag::GENERIC - ) - ).WillOnce(Return(Maybe(reply_msg))); + expectNewConfigRequest("{\n \"id\": 1,\n \"policy_version\": \"1.0.2,1.0.2\"\n}", reply_msg); EXPECT_CALL( mock_shell_cmd, @@ -732,24 +696,11 @@ TEST_F(ServiceControllerTest, noPolicyUpdate) EXPECT_CALL(mock_orchestration_status, setServiceConfiguration("l4_firewall", l4_firewall_policy_path, OrchestrationStatusConfigType::POLICY)); + EXPECT_CALL(mock_orchestration_tools, calculateChecksum(Package::ChecksumTypes::MD5, file_name)) + .WillOnce(Return(version_value)); + string reply_msg = "{\"id\": 1, \"error\": false, \"finished\": true, \"error_message\": \"\"}"; - Flags conn_flags; - conn_flags.setFlag(MessageConnConfig::ONE_TIME_CONN); - EXPECT_CALL( - mock_message, - sendMessage( - true, - "{\n \"id\": 1,\n \"policy_version\": \"1.0.2\"\n}", - I_Messaging::Method::POST, - string("127.0.0.1"), - l4_firewall_service_port, - conn_flags, - string("/set-new-configuration"), - string(), - _, - MessageTypeTag::GENERIC - ) - ).WillOnce(Return(Maybe(reply_msg))); + expectNewConfigRequest("{\n \"id\": 1,\n \"policy_version\": \"1.0.2,1.0.2\"\n}", reply_msg); EXPECT_CALL( mock_shell_cmd, @@ -818,6 +769,9 @@ TEST_F(ServiceControllerTest, SettingsAndPolicyUpdateCombinations) EXPECT_EQ(i_service_controller->getPolicyVersion(), ""); + EXPECT_CALL(mock_orchestration_tools, calculateChecksum(Package::ChecksumTypes::MD5, file_name)) + .WillOnce(Return(version_value)); + 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)); @@ -835,24 +789,7 @@ TEST_F(ServiceControllerTest, SettingsAndPolicyUpdateCombinations) string general_settings_path = "/my/settings/path"; string reply_msg1 = "{\"id\": 1, \"error\": false, \"finished\": true, \"error_message\": \"\"}"; - - Flags conn_flags; - conn_flags.setFlag(MessageConnConfig::ONE_TIME_CONN); - EXPECT_CALL( - mock_message, - sendMessage( - true, - "{\n \"id\": 1,\n \"policy_version\": \"1.0.2\"\n}", - I_Messaging::Method::POST, - string("127.0.0.1"), - l4_firewall_service_port, - conn_flags, - string("/set-new-configuration"), - string(), - _, - MessageTypeTag::GENERIC - ) - ).WillOnce(Return(Maybe(reply_msg1))); + expectNewConfigRequest("{\n \"id\": 1,\n \"policy_version\": \"1.0.2,1.0.2\"\n}", reply_msg1); // both policy and settings now being updated EXPECT_TRUE(i_service_controller->updateServiceConfiguration(file_name, general_settings_path).ok()); @@ -871,26 +808,14 @@ TEST_F(ServiceControllerTest, SettingsAndPolicyUpdateCombinations) EXPECT_CALL(mock_orchestration_tools, copyFile(file_name, policy_file_path)).WillOnce(Return(true)); EXPECT_CALL(mock_orchestration_status, setServiceConfiguration("l4_firewall", l4_firewall_policy_path, OrchestrationStatusConfigType::POLICY)); + + EXPECT_CALL(mock_orchestration_tools, calculateChecksum(Package::ChecksumTypes::MD5, file_name)) + .WillOnce(Return(version_value)); + general_settings_path += "/new"; string reply_msg2 = "{\"id\": 2, \"error\": false, \"finished\": true, \"error_message\": \"\"}"; - Flags conn_flags2; - conn_flags2.setFlag(MessageConnConfig::ONE_TIME_CONN); - EXPECT_CALL( - mock_message, - sendMessage( - true, - "{\n \"id\": 2,\n \"policy_version\": \"1.0.2\"\n}", - I_Messaging::Method::POST, - string("127.0.0.1"), - l4_firewall_service_port, - conn_flags2, - string("/set-new-configuration"), - string(), - _, - MessageTypeTag::GENERIC - ) - ).WillRepeatedly(Return(Maybe(reply_msg2))); + expectNewConfigRequest("{\n \"id\": 2,\n \"policy_version\": \"1.0.2,1.0.2\"\n}", reply_msg2); EXPECT_TRUE(i_service_controller->updateServiceConfiguration(file_name, general_settings_path).ok()); EXPECT_EQ(i_service_controller->getPolicyVersion(), version_value); @@ -964,6 +889,9 @@ TEST_F(ServiceControllerTest, backup) EXPECT_CALL(mock_orchestration_status, setServiceConfiguration("l4_firewall", l4_firewall_policy_path, OrchestrationStatusConfigType::POLICY)); + EXPECT_CALL(mock_orchestration_tools, calculateChecksum(Package::ChecksumTypes::MD5, file_name)) + .WillOnce(Return(version_value)); + EXPECT_CALL( mock_orchestration_tools, copyFile(l4_firewall_policy_path, l4_firewall_policy_path + backup_extension) @@ -988,21 +916,8 @@ TEST_F(ServiceControllerTest, backup) ).WillRepeatedly(Return(string("registered and running"))); string reply_msg = "{\"id\": 1, \"error\": false, \"finished\": true, \"error_message\": \"\"}"; - EXPECT_CALL( - mock_message, - sendMessage( - _, - _, - _, - "127.0.0.1", - l4_firewall_service_port, - _, - "/set-new-configuration", - _, - _, - MessageTypeTag::GENERIC - ) - ).WillOnce(Return(Maybe(reply_msg))); + EXPECT_CALL(mock_message, sendSyncMessage(_, "/set-new-configuration", _, _, _)) + .WillOnce(Return(HTTPResponse(HTTPStatusCode::HTTP_OK, reply_msg))); EXPECT_EQ(i_service_controller->getPolicyVersion(), ""); EXPECT_TRUE(i_service_controller->updateServiceConfiguration(file_name, "").ok()); @@ -1077,6 +992,9 @@ TEST_F(ServiceControllerTest, backup_file_doesnt_exist) EXPECT_CALL(mock_orchestration_status, setServiceConfiguration("l4_firewall", l4_firewall_policy_path, OrchestrationStatusConfigType::POLICY)); + EXPECT_CALL(mock_orchestration_tools, calculateChecksum(Package::ChecksumTypes::MD5, file_name)) + .WillOnce(Return(version_value)); + EXPECT_CALL( mock_orchestration_tools, copyFile(l4_firewall_policy_path, l4_firewall_policy_path + backup_extension) @@ -1103,21 +1021,7 @@ TEST_F(ServiceControllerTest, backup_file_doesnt_exist) ).WillRepeatedly(Return(string("registered and running"))); string reply_msg = "{\"id\": 1, \"error\": false, \"finished\": true, \"error_message\": \"\"}"; - EXPECT_CALL( - mock_message, - sendMessage( - _, - _, - _, - "127.0.0.1", - l4_firewall_service_port, - _, - "/set-new-configuration", - _, - _, - MessageTypeTag::GENERIC - ) - ).WillOnce(Return(Maybe(reply_msg))); + expectNewConfigRequest("{\n \"id\": 1,\n \"policy_version\": \"1.0.2,1.0.2\"\n}", reply_msg); EXPECT_EQ(i_service_controller->getPolicyVersion(), ""); EXPECT_TRUE(i_service_controller->updateServiceConfiguration(file_name, "").ok()); @@ -1192,6 +1096,9 @@ TEST_F(ServiceControllerTest, backupAttempts) EXPECT_CALL(mock_orchestration_status, setServiceConfiguration("l4_firewall", l4_firewall_policy_path, OrchestrationStatusConfigType::POLICY)); + EXPECT_CALL(mock_orchestration_tools, calculateChecksum(Package::ChecksumTypes::MD5, file_name)) + .WillOnce(Return(version_value)); + EXPECT_CALL( mock_orchestration_tools, copyFile(l4_firewall_policy_path, l4_firewall_policy_path + backup_extension) @@ -1218,21 +1125,7 @@ TEST_F(ServiceControllerTest, backupAttempts) ).WillRepeatedly(Return(string("registered and running"))); string reply_msg = "{\"id\": 1, \"error\": false, \"finished\": true, \"error_message\": \"\"}"; - EXPECT_CALL( - mock_message, - sendMessage( - _, - _, - _, - "127.0.0.1", - l4_firewall_service_port, - _, - "/set-new-configuration", - _, - _, - MessageTypeTag::GENERIC - ) - ).WillOnce(Return(Maybe(reply_msg))); + expectNewConfigRequest("{\n \"id\": 1,\n \"policy_version\": \"1.0.2,1.0.2\"\n}", reply_msg); EXPECT_CALL(mock_ml, yield(false)).Times(2); EXPECT_CALL(mock_orchestration_tools, copyFile(file_name, policy_file_path)).WillOnce(Return(true)); @@ -1316,6 +1209,9 @@ TEST_F(ServiceControllerTest, MultiUpdateConfiguration) EXPECT_CALL(mock_orchestration_status, setServiceConfiguration("orchestration", orchestration_policy_path, OrchestrationStatusConfigType::POLICY)); + EXPECT_CALL(mock_orchestration_tools, calculateChecksum(Package::ChecksumTypes::MD5, file_name)) + .WillOnce(Return(version_value)); + 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)) @@ -1336,23 +1232,7 @@ TEST_F(ServiceControllerTest, MultiUpdateConfiguration) ).WillRepeatedly(Return(string("registered and running"))); string reply_msg = "{\"id\": 1, \"error\": false, \"finished\": true, \"error_message\": \"\"}"; - Flags conn_flags; - conn_flags.setFlag(MessageConnConfig::ONE_TIME_CONN); - EXPECT_CALL( - mock_message, - sendMessage( - true, - "{\n \"id\": 1,\n \"policy_version\": \"1.0.2\"\n}", - I_Messaging::Method::POST, - string("127.0.0.1"), - l4_firewall_service_port, - conn_flags, - string("/set-new-configuration"), - string(), - _, - MessageTypeTag::GENERIC - ) - ).WillOnce(Return(Maybe(reply_msg))); + expectNewConfigRequest("{\n \"id\": 1,\n \"policy_version\": \"1.0.2,1.0.2\"\n}", reply_msg); EXPECT_TRUE(i_service_controller->updateServiceConfiguration(file_name, "").ok()); set changed_policies = { @@ -1389,6 +1269,9 @@ TEST_F(ServiceControllerTest, emptyServices) EXPECT_CALL(mock_orchestration_tools, copyFile(file_name, policy_file_path)).WillOnce(Return(true)); EXPECT_CALL(mock_orchestration_tools, doesFileExist(policy_file_path)).WillOnce(Return(true)); + EXPECT_CALL(mock_orchestration_tools, calculateChecksum(Package::ChecksumTypes::MD5, file_name)) + .WillOnce(Return(version_value)); + EXPECT_TRUE(i_service_controller->updateServiceConfiguration(file_name, "").ok()); } @@ -1440,6 +1323,9 @@ TEST_F(ServiceControllerTest, failingWhileLoadingCurrentConfiguration) .WillOnce(Return(json_parser_return)); EXPECT_CALL(mock_orchestration_tools, doesFileExist(l4_firewall_policy_path)).WillOnce(Return(true)); EXPECT_CALL(mock_orchestration_tools, readFile(l4_firewall_policy_path)).WillOnce(Return(err)); + + EXPECT_CALL(mock_orchestration_tools, calculateChecksum(Package::ChecksumTypes::MD5, file_name)) + .WillOnce(Return(version_value)); EXPECT_FALSE(i_service_controller->updateServiceConfiguration(file_name, "").ok()); } @@ -1509,6 +1395,8 @@ TEST_F(ServiceControllerTest, failingWhileCopyingCurrentConfiguration) ); EXPECT_CALL(mock_orchestration_tools, doesFileExist(l4_firewall_policy_path)).WillOnce(Return(true)); EXPECT_CALL(mock_orchestration_tools, readFile(l4_firewall_policy_path)).WillOnce(Return(old_configuration)); + EXPECT_CALL(mock_orchestration_tools, calculateChecksum(Package::ChecksumTypes::MD5, file_name)) + .WillOnce(Return(version_value)); EXPECT_CALL( mock_orchestration_tools, copyFile(l4_firewall_policy_path, l4_firewall_policy_path + backup_extension) @@ -1578,6 +1466,9 @@ TEST_F(ServiceControllerTest, ErrorUpdateConfigurationRest) setServiceConfiguration("l4_firewall", l4_firewall_policy_path, OrchestrationStatusConfigType::POLICY) ); + EXPECT_CALL(mock_orchestration_tools, calculateChecksum(Package::ChecksumTypes::MD5, file_name)) + .WillOnce(Return(version_value)); + EXPECT_EQ(i_service_controller->getPolicyVersion(), ""); EXPECT_TRUE(i_service_controller->isServiceInstalled("family1_id2")); @@ -1672,6 +1563,8 @@ TEST_F(ServiceControllerTest, errorWhileWrtingNewConfiguration) ); EXPECT_CALL(mock_orchestration_tools, doesFileExist(l4_firewall_policy_path)).WillOnce(Return(true)); EXPECT_CALL(mock_orchestration_tools, readFile(l4_firewall_policy_path)).WillOnce(Return(old_configuration)); + EXPECT_CALL(mock_orchestration_tools, calculateChecksum(Package::ChecksumTypes::MD5, file_name)) + .WillOnce(Return(version_value)); EXPECT_CALL( mock_orchestration_tools, copyFile(l4_firewall_policy_path, l4_firewall_policy_path + backup_extension) @@ -1710,21 +1603,7 @@ TEST_F(ServiceControllerTest, testMultitenantConfFiles) EXPECT_CALL(tenant_manager, getInstances("tenant2", "1235")).WillOnce(Return(empty_ids)); string reply_msg = "{\"id\": 1, \"error\": false, \"finished\": true, \"error_message\": \"\"}"; - EXPECT_CALL( - mock_message, - sendMessage( - true, - "{\n \"id\": 1,\n \"policy_version\": \"1.0.2\"\n}", - _, - string("127.0.0.1"), - l4_firewall_service_port, - _, - string("/set-new-configuration"), - _, - _, - _ - ) - ).WillOnce(Return(Maybe(reply_msg))); + expectNewConfigRequest("{\n \"id\": 1,\n \"policy_version\": \"1.0.2,1.0.2\"\n}", reply_msg); for(auto entry : tenant_files_input) { auto tenant = entry.first.first; @@ -1801,6 +1680,9 @@ TEST_F(ServiceControllerTest, testMultitenantConfFiles) "l4_firewall", l4_firewall_policy_path_new, OrchestrationStatusConfigType::POLICY) ); + EXPECT_CALL(mock_orchestration_tools, calculateChecksum(Package::ChecksumTypes::MD5, conf_file_name)) + .WillRepeatedly(Return(version_value)); + string new_policy_file_path = "/etc/cp/conf/tenant_" + tenant + "_profile_" + profile + "/" + "policy.json"; EXPECT_CALL(mock_orchestration_tools, copyFile(new_policy_file_path, new_policy_file_path + backup_extension)) .WillOnce(Return(true)); @@ -1906,6 +1788,9 @@ TEST_F(ServiceControllerTest, test_delayed_reconf) EXPECT_CALL(mock_orchestration_status, setServiceConfiguration("l4_firewall", l4_firewall_policy_path, OrchestrationStatusConfigType::POLICY)); + EXPECT_CALL(mock_orchestration_tools, calculateChecksum(Package::ChecksumTypes::MD5, file_name)) + .WillOnce(Return(version_value)); + 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)); @@ -1934,23 +1819,7 @@ TEST_F(ServiceControllerTest, test_delayed_reconf) << " \"error_message\": \"\"" << "}"; - Flags conn_flags; - conn_flags.setFlag(MessageConnConfig::ONE_TIME_CONN); - EXPECT_CALL( - mock_message, - sendMessage( - true, - "{\n \"id\": 1,\n \"policy_version\": \"1.0.2\"\n}", - I_Messaging::Method::POST, - string("127.0.0.1"), - l4_firewall_service_port, - conn_flags, - string("/set-new-configuration"), - string(), - _, - MessageTypeTag::GENERIC - ) - ).WillOnce(Return(Maybe(reply_msg))); + expectNewConfigRequest("{\n \"id\": 1,\n \"policy_version\": \"1.0.2,1.0.2\"\n}", reply_msg); auto func = [&] (chrono::microseconds) { set_reconf_status->performRestCall(reconf_status); }; EXPECT_CALL(mock_ml, yield(chrono::microseconds(2000000))).WillOnce(Invoke(func)); diff --git a/components/security_apps/orchestration/update_communication/declarative_policy_utils.cc b/components/security_apps/orchestration/update_communication/declarative_policy_utils.cc old mode 100755 new mode 100644 index e00604a..1d85a0d --- a/components/security_apps/orchestration/update_communication/declarative_policy_utils.cc +++ b/components/security_apps/orchestration/update_communication/declarative_policy_utils.cc @@ -27,7 +27,7 @@ DeclarativePolicyUtils::init() auto mainloop = Singleton::Consume::by(); mainloop->addRecurringRoutine( I_MainLoop::RoutineType::Offline, - chrono::minutes(1), + chrono::seconds(30), [&] () { periodicPolicyLoad(); }, "Automatic Policy Loading" ); diff --git a/components/security_apps/orchestration/update_communication/fog_communication.cc b/components/security_apps/orchestration/update_communication/fog_communication.cc index 21bac79..c70ea54 100755 --- a/components/security_apps/orchestration/update_communication/fog_communication.cc +++ b/components/security_apps/orchestration/update_communication/fog_communication.cc @@ -32,7 +32,7 @@ void FogCommunication::init() { FogAuthenticator::init(); - declarative_policy_utils.init(); + i_declarative_policy = Singleton::Consume::from(); } Maybe @@ -67,15 +67,15 @@ FogCommunication::getUpdate(CheckUpdateRequest &request) Maybe maybe_new_data = request.getData(); string data_checksum = maybe_new_data.ok() ? maybe_new_data.unpack() : ""; - if (declarative_policy_utils.shouldApplyPolicy()) { - string policy_response = declarative_policy_utils.getUpdate(request); + if (i_declarative_policy->shouldApplyPolicy()) { + string policy_response = i_declarative_policy->getUpdate(request); if (!policy_response.empty()) { dbgTrace(D_ORCHESTRATOR) << "Apply policy - declarative mode"; auto agent_details = Singleton::Consume::by(); auto maybe_fog_address = agent_details->getFogDomain(); string fog_address = maybe_fog_address.ok() ? maybe_fog_address.unpack() : ""; - declarative_policy_utils.sendUpdatesToFog( + i_declarative_policy->sendUpdatesToFog( unpacked_access_token, agent_details->getTenantId(), agent_details->getProfileId(), @@ -83,7 +83,6 @@ FogCommunication::getUpdate(CheckUpdateRequest &request) ); } request = CheckUpdateRequest(manifest_checksum, policy_response, settings_checksum, data_checksum, "", ""); - declarative_policy_utils.turnOffApplyPolicyFlag(); } else { request = CheckUpdateRequest(manifest_checksum, "", settings_checksum, data_checksum, "", ""); } @@ -103,7 +102,7 @@ FogCommunication::downloadAttributeFile(const GetResourceFile &resourse_file) string policy_mgmt_mode = getSettingWithDefault("management", "profileManagedMode"); if (policy_mgmt_mode == "declarative" && resourse_file.getFileName() =="policy") { dbgDebug(D_ORCHESTRATOR) << "Download policy on declarative mode - returnig the local policy"; - return declarative_policy_utils.getCurrPolicy(); + return i_declarative_policy->getCurrPolicy(); } static const string file_attribute_str = "/api/v2/agents/resources/"; Maybe attribute_file = Singleton::Consume::by()->downloadFile( diff --git a/components/security_apps/orchestration/update_communication/hybrid_communication.cc b/components/security_apps/orchestration/update_communication/hybrid_communication.cc index 966f913..4d594fe 100755 --- a/components/security_apps/orchestration/update_communication/hybrid_communication.cc +++ b/components/security_apps/orchestration/update_communication/hybrid_communication.cc @@ -35,7 +35,7 @@ void HybridCommunication::init() { FogAuthenticator::init(); - declarative_policy_utils.init(); + i_declarative_policy = Singleton::Consume::from(); dbgTrace(D_ORCHESTRATOR) << "Initializing the Hybrid Communication Component"; if (getConfigurationFlag("otp") != "") { otp = getConfigurationFlag("otp"); @@ -69,14 +69,14 @@ HybridCommunication::getUpdate(CheckUpdateRequest &request) dbgWarning(D_ORCHESTRATOR) << "Acccess Token not available."; } - if (!declarative_policy_utils.shouldApplyPolicy()) { + if (!i_declarative_policy->shouldApplyPolicy()) { request = CheckUpdateRequest(manifest_checksum, "", "", "", "", ""); return Maybe(); } dbgTrace(D_ORCHESTRATOR) << "Getting policy update in Hybrid Communication"; - string policy_response = declarative_policy_utils.getUpdate(request); + string policy_response = i_declarative_policy->getUpdate(request); auto env = Singleton::Consume::by()->getEnvType(); if (env == EnvType::K8S && !policy_response.empty()) { @@ -123,7 +123,6 @@ HybridCommunication::getUpdate(CheckUpdateRequest &request) } request = CheckUpdateRequest(manifest_checksum, policy_response, "", "", "", ""); - declarative_policy_utils.turnOffApplyPolicyFlag(); return Maybe(); } @@ -136,7 +135,7 @@ HybridCommunication::downloadAttributeFile(const GetResourceFile &resourse_file) << resourse_file.getFileName(); if (resourse_file.getFileName() =="policy") { - return declarative_policy_utils.getCurrPolicy(); + return i_declarative_policy->getCurrPolicy(); } if (resourse_file.getFileName() == "manifest") { if (!access_token.ok()) return genError("Acccess Token not available."); diff --git a/components/security_apps/orchestration/update_communication/update_communication.cc b/components/security_apps/orchestration/update_communication/update_communication.cc index 3ecfd26..b27db9f 100755 --- a/components/security_apps/orchestration/update_communication/update_communication.cc +++ b/components/security_apps/orchestration/update_communication/update_communication.cc @@ -57,6 +57,7 @@ public: void init() { + declarative_policy_utils.init(); auto rest = Singleton::Consume::by(); rest->addRestCall(RestAction::SET, "orchestration-mode"); setMode(); @@ -104,22 +105,17 @@ private: { if (getConfigurationFlag("orchestration-mode") == "offline_mode") { i_update_comm_impl = make_unique(); - LocalCommunication *local_comm = static_cast(i_update_comm_impl.get()); - local_comm->init(); - return; } else if (getConfigurationFlag("orchestration-mode") == "hybrid_mode") { i_update_comm_impl = make_unique(); - HybridCommunication *local_comm = static_cast(i_update_comm_impl.get()); - local_comm->init(); - return; + } else { + i_update_comm_impl = make_unique(); } - i_update_comm_impl = make_unique(); - FogCommunication *fog_comm = static_cast(i_update_comm_impl.get()); - fog_comm->init(); + i_update_comm_impl->init(); } std::unique_ptr i_update_comm_impl = nullptr; + DeclarativePolicyUtils declarative_policy_utils; S2C_LABEL_PARAM(string, status, "status"); }; diff --git a/components/security_apps/orchestration/update_communication/update_communication_ut/CMakeLists.txt b/components/security_apps/orchestration/update_communication/update_communication_ut/CMakeLists.txt index ebeaffd..5fa6a04 100755 --- a/components/security_apps/orchestration/update_communication/update_communication_ut/CMakeLists.txt +++ b/components/security_apps/orchestration/update_communication/update_communication_ut/CMakeLists.txt @@ -2,6 +2,6 @@ link_directories(${BOOST_ROOT}/lib) add_unit_test( update_communication_ut - "local_communication_ut.cc" + "local_communication_ut.cc;fog_communication_ut.cc" "rest;version;orchestration_modules;update_communication;singleton;config;metric;event_is;logging;agent_details;-lboost_regex;local_policy_mgmt_gen;connkey;" ) diff --git a/components/security_apps/orchestration/update_communication/update_communication_ut/fog_communication_ut.cc b/components/security_apps/orchestration/update_communication/update_communication_ut/fog_communication_ut.cc new file mode 100755 index 0000000..b0f9ed6 --- /dev/null +++ b/components/security_apps/orchestration/update_communication/update_communication_ut/fog_communication_ut.cc @@ -0,0 +1,2152 @@ +#ifndef DISABLE_APPSEC_DATA_ENCRYPTION +#include + +#include "fog_communication.h" +#include "version.h" +#include "cptest.h" +#include "mainloop.h" +#include "time_proxy.h" +#include "config.h" +#include "config_component.h" +#include "agent_details.h" +#include "declarative_policy_utils.h" +#include "local_policy_mgmt_gen.h" +#include "mock/mock_env_details.h" + +#include "mock/mock_orchestration_status.h" +#include "mock/mock_orchestration_tools.h" +#include "mock/mock_details_resolver.h" +#include "mock/mock_messaging.h" +#include "mock/mock_messaging.h" +#include "mock/mock_time_get.h" +#include "mock/mock_mainloop.h" +#include "mock/mock_shell_cmd.h" +#include "mock/mock_encryptor.h" +#include "mock/mock_rest_api.h" + +using namespace std; +using namespace testing; + +ostream & +operator<<(ostream &os, const Maybe> &) +{ + return os; +} + +unique_ptr rest_handler; +unique_ptr declare_variable; +unique_ptr apply_policy; + +bool restHandler(const unique_ptr &p) { rest_handler = p->getRest(); return true; } +bool declareVariable(const unique_ptr &p) { declare_variable = p->getRest(); return true; } +bool applyPolicy(const unique_ptr &p) { apply_policy = p->getRest(); return true; } + +class FogCommunicationTest: public Test +{ +public: + FogCommunicationTest() + { + EXPECT_CALL(mock_rs, mockRestCall(RestAction::SHOW, "version-info", _)).WillOnce(Return(true)); + EXPECT_CALL( + mock_rs, + mockRestCall(RestAction::SHOW, "access-token", _) + ).WillOnce(WithArg<2>(Invoke(restHandler))); + + EXPECT_CALL( + mock_rs, + mockRestCall(RestAction::ADD, "declare-boolean-variable", _) + ).WillOnce(WithArg<2>(Invoke(declareVariable))); + + EXPECT_CALL(mock_rs, mockRestCall(RestAction::SET, "apply-policy", _)) + .WillOnce(WithArg<2>(Invoke(applyPolicy)) + ); + + env.preload(); + env.init(); + Version::init(); + declarative_policy.init(); + } + + ~FogCommunicationTest() + { + env.fini(); + } + + void + init() + { + fog_communication.init(); + } + + void + preload() + { + fog_communication.preload(); + } + + Maybe + sendPolicyVersion(const string &policy_version, const string &policy_versions) + { + return fog_communication.sendPolicyVersion(policy_version, policy_versions); + } + + Maybe + authenticateAgent() + { + return fog_communication.authenticateAgent(); + } + + Maybe + downloadAttributeFile(const GetResourceFile &resourse_file, const string &file_path) + { + return fog_communication.downloadAttributeFile(resourse_file, file_path); + } + + void setFogExtension(const string &ex) + { + fog_communication.setAddressExtenesion(ex); + } + + Maybe + checkUpdate(CheckUpdateRequest &req) + { + return fog_communication.getUpdate(req); + } + + void + expectTokenRequest() + { + HTTPResponse res( + HTTPStatusCode::HTTP_OK, + string( + "{" + " \"access_token\": \"" + clear_access_token + "\"," + " \"token_type\": \"basic\"," + " \"expires_in\": 100," + " \"scope\": \"idk\"," + " \"uuid\": \"user_id\"," + " \"jti\": \"jti-id\"" + "}" + ) + ); + + EXPECT_CALL( + mock_message, + sendSyncMessage( + HTTPMethod::POST, + "/oauth/token?grant_type=client_credentials", + "", + _, + _ + ) + ).WillOnce(Return(res)); + } + + void + expectAuthenticationData(const string &req_body) + { + EXPECT_CALL( + mock_message, + sendSyncMessage( + HTTPMethod::POST, + "/agents", + req_body, + _, + _ + ) + ).WillOnce(Return(HTTPResponse(HTTPStatusCode::HTTP_OK, clear_cred_body))); + } + + void + expectCheckupdateRequest(const string &req_body, const string &res_body) + { + EXPECT_CALL( + mock_message, + sendSyncMessage( + HTTPMethod::POST, + "/api/v2/agents/resources", + req_body, + _, + _ + ) + ).WillOnce(Return(HTTPResponse(HTTPStatusCode::HTTP_OK, res_body))); + } + + void + setUpgradeFields(CheckUpdateRequest &req) + { + auto upgrade_mode = getSettingWithDefault("manual", "upgradeMode"); + if (upgrade_mode != "scheduled") { + req.setUpgradeFields(upgrade_mode); + } else { + req.setUpgradeFields( + upgrade_mode, + getSettingWithDefault("0:00", "upgradeTime"), + getSettingWithDefault(4, "upgradeDurationHours"), + getSettingWithDefault>({}, "upgradeDay") + ); + } + } + + ::Environment env; + AgentDetails agent_details; + ConfigComponent config_comp; + LocalPolicyMgmtGenerator local_policy_gen; + DeclarativePolicyUtils declarative_policy; + StrictMock mock_env_details; + StrictMock mock_rs; + StrictMock mock_ml; + StrictMock mock_message; + StrictMock mock_ot; + StrictMock mock_status; + StrictMock mock_details_resolver; + NiceMock time; + StrictMock mock_shell_cmd; + StrictMock mock_encryptor; + string clear_access_token = "BEST ACCESS TOKEN EVER"; + string base64_access_token = "dsadadsadsa"; + string agent_id = "35f5a31a-d333-47bf-bc61-6912cdbd96bc"; + string profile_id = "077aa3c2-82e0-405f-802b-225dc3c16bf3"; + string tenant_id = "7bb5aab4-cc81-4724-bc87-9c0616cd562d"; + string encrypted_access_token = "dsadadsadsa"; + Maybe mb_encrypted_access_token = encrypted_access_token; + string clear_cred_body = + "{" + " \"client_id\":\"user id\"," + " \"shared_secret\": \"best shared secret\"," + " \"tenantId\": \"" + tenant_id + "\"," + " \"profileId\": \"" + profile_id + "\"," + " \"agentId\": \"" + agent_id + "\"" + "}"; + string clear_cred = "{\n \"client_id\": \"user id\",\n \"shared_secret\": \"best shared secret\"\n}"; + string encrypted_cred = "adsadasdsadadsa"; // Not real base64, just for test + string clear_otp = + "{\n" + " \"registration type\": \"token\",\n" + " \"registration data\": \"This is the best OTP token\",\n" + " \"expired\": false\n" + "}\n"; + + string base64_otp = "adsadasdsadadsa"; // Not real base64, just for test + Maybe mb_base64_otp = base64_otp; + string data_path = "/etc/cp/data/"; + + const string required_apps_file_path = "/etc/cp/conf/support-practices.txt"; + +private: + FogCommunication fog_communication; +}; + +TEST_F(FogCommunicationTest, doNothing) +{ + EXPECT_CALL(mock_ot, doesFileExist(required_apps_file_path)).WillOnce(Return(false)); + init(); +} + +TEST_F(FogCommunicationTest, register_config) +{ + EXPECT_CALL(mock_ot, doesFileExist(required_apps_file_path)).WillOnce(Return(false)); + init(); + + preload(); + string config_json = + "{\n" + " \"orchestration\": {\n" + " \"OTP Token Path\": [\n" + " {\n" + " \"context\": \"All()\",\n" + " \"value\": \"ABC\"\n" + " }\n" + " ],\n" + " \"User Credentials Path\": [\n" + " {\n" + " \"context\": \"All()\",\n" + " \"value\": \"qwe\"\n" + " }\n" + " ],\n" + " \"Agent type\": [\n" + " {\n" + " \"context\": \"All()\",\n" + " \"value\": \"CCCC\"\n" + " }\n" + " ]\n" + " }\n" + "}"; + istringstream ss(config_json); + Singleton::Consume::from(config_comp)->loadConfiguration(ss); + + EXPECT_THAT(getConfiguration("orchestration", "OTP Token Path"), IsValue("ABC")); + EXPECT_THAT(getConfiguration("orchestration", "User Credentials Path"), IsValue("qwe")); + EXPECT_THAT(getConfiguration("orchestration", "Agent type"), IsValue("CCCC")); +} + +TEST_F(FogCommunicationTest, authenticateAgentFromGivenCredentials) +{ + EXPECT_CALL(mock_ot, doesFileExist(required_apps_file_path)).WillOnce(Return(false)); + init(); + // Reading user cred + EXPECT_CALL(mock_ot, readFile(data_path + user_cred_file_name)).WillOnce(Return(encrypted_cred)); + EXPECT_CALL(mock_encryptor, aes256DecryptWithSizePad(encrypted_cred)).WillOnce(Return(clear_cred)); + EXPECT_CALL( + mock_status, + setFieldStatus(OrchestrationStatusFieldType::REGISTRATION, OrchestrationStatusResult::SUCCESS, "") + ).Times(2); + // Creating the session token routine + EXPECT_CALL(mock_ml, doesRoutineExist(0)).WillOnce(Return(false)); + I_MainLoop::Routine routine; + EXPECT_CALL(mock_ml, addOneTimeRoutine(I_MainLoop::RoutineType::RealTime, _, _, true)) + .WillOnce(DoAll(SaveArg<1>(&routine), Return(1))); + EXPECT_CALL(mock_ml, yield(chrono::microseconds(11000000))); + EXPECT_FALSE(authenticateAgent().ok()); + + // Looping of the routine + EXPECT_CALL(mock_ot, base64Encode("user id:best shared secret")) + .WillOnce(Return(string("dXNlciBpZDpiZXN0IHNoYXJlZCBzZWNyZXQ="))); + + expectTokenRequest(); + EXPECT_CALL( + mock_encryptor, + aes256EncryptWithSizePad(clear_access_token)).WillOnce(Return(encrypted_access_token) + ); + EXPECT_CALL( + mock_ot, + writeFile(encrypted_access_token, data_path + session_token_file_name, false)).WillOnce(Return(true) + ); + EXPECT_CALL(mock_ml, yield(A())).WillOnce( + Invoke( + [] (chrono::microseconds microseconds) + { + EXPECT_EQ(10000000, microseconds.count()); // Validate short expiration time, mininum is 10 sec + throw invalid_argument("stop while loop"); + } + ) + ); + try { + routine(); + } catch (const invalid_argument& e) {} + EXPECT_CALL(mock_ml, doesRoutineExist(1)).WillOnce(Return(true)); + EXPECT_TRUE(authenticateAgent().ok()); +} + +TEST_F(FogCommunicationTest, authenticateAgentFromOTPToken) +{ + EXPECT_CALL(mock_ot, doesFileExist(required_apps_file_path)).WillOnce(Return(false)); + init(); + // Fog ext + setFogExtension("test"); + + // Reading user cred + Maybe no_cred_err(genError("No Credentials file")); + EXPECT_CALL(mock_ot, readFile(data_path + user_cred_file_name)).WillOnce(Return(no_cred_err)); + + // Reading OTP + EXPECT_CALL(mock_ot, readFile("/etc/cp/conf/registration-data.json")).WillOnce(Return(base64_otp)); + EXPECT_CALL(mock_ot, base64Decode(base64_otp)).WillOnce(Return(clear_otp)); + EXPECT_CALL(mock_ot, removeFile("/etc/cp/conf/registration-data.json")).WillOnce(Return(true)); + EXPECT_CALL(mock_status, setAgentDetails(agent_id, profile_id, tenant_id)); + EXPECT_CALL(mock_status, + setRegistrationDetails("smartmeter", "Embedded", "gaia", "x86_64")); + EXPECT_CALL( + mock_status, + setFieldStatus(OrchestrationStatusFieldType::REGISTRATION, OrchestrationStatusResult::SUCCESS, "") + ).Times(2); + + Maybe> no_nginx(genError("No nginx")); + EXPECT_CALL(mock_details_resolver, getHostname()).WillOnce(Return(string("smartmeter"))); + EXPECT_CALL(mock_details_resolver, getPlatform()).WillOnce(Return(string("gaia"))); + EXPECT_CALL(mock_details_resolver, getArch()).WillOnce(Return(string("x86_64"))); + EXPECT_CALL(mock_details_resolver, isReverseProxy()).WillOnce(Return(true)); + EXPECT_CALL(mock_details_resolver, isKernelVersion3OrHigher()).WillOnce(Return(true)); + EXPECT_CALL(mock_details_resolver, isGwNotVsx()).WillOnce(Return(true)); + EXPECT_CALL(mock_details_resolver, isVersionEqualOrAboveR8110()).WillOnce(Return(true)); + + map resolved_mgmt_details({{"cpProductIntegrationMgmtObjectType", "management"}}); + EXPECT_CALL(mock_details_resolver, getResolvedDetails()).WillOnce(Return(resolved_mgmt_details)); + + EXPECT_CALL(mock_details_resolver, parseNginxMetadata()).WillOnce(Return(no_nginx)); + EXPECT_CALL(mock_details_resolver, getAgentVersion()) + .WillOnce(Return(Version::getFullVersion())) + .WillOnce(Return(Version::getFullVersion())); + + expectAuthenticationData( + "{\n" + " \"authenticationData\": [\n" + " {\n" + " \"authenticationMethod\": \"token\",\n" + " \"data\": \"This is the best OTP token\"\n" + " }\n" + " ],\n" + " \"metaData\": {\n" + " \"agentName\": \"smartmeter\",\n" + " \"agentType\": \"Embedded\",\n" + " \"platform\": \"gaia\",\n" + " \"architecture\": \"x86_64\",\n" + " \"agentVersion\": \"" + Version::getFullVersion() + "\",\n" + " \"additionalMetaData\": {\n" + " \"agent_version\": \"" + Version::getFullVersion() + "\",\n" + " \"cpProductIntegrationMgmtObjectType\": \"management\",\n" + " \"isGwNotVsx\": \"true\",\n" + " \"isKernelVersion3OrHigher\": \"true\",\n" + " \"isVersionEqualOrAboveR8110\": \"true\",\n" + " \"managedMode\": \"management\",\n" + " \"reverse_proxy\": \"true\",\n" + " \"userEdition\": \"PrEm1um%\"\n" + " }\n" + " }\n" + "}" + ); + + // Saving cred + EXPECT_CALL(mock_encryptor, aes256EncryptWithSizePad(clear_cred)).WillOnce(Return(mb_base64_otp)); + EXPECT_CALL(mock_ot, writeFile(base64_otp, data_path + user_cred_file_name, false)).WillOnce(Return(true)); + + // Creating the session token routine + EXPECT_CALL(mock_ml, doesRoutineExist(0)).WillOnce(Return(false)); + I_MainLoop::Routine routine; + EXPECT_CALL(mock_ml, addOneTimeRoutine(I_MainLoop::RoutineType::RealTime, _, _, true)) + .WillOnce(DoAll(SaveArg<1>(&routine), Return(1))); + EXPECT_CALL(mock_ml, yield(chrono::microseconds(11000000))); + EXPECT_FALSE(authenticateAgent().ok()); + + // Looping the routine + EXPECT_CALL(mock_ot, base64Encode("user id:best shared secret")) + .WillOnce(Return(string("dXNlciBpZDpiZXN0IHNoYXJlZCBzZWNyZXQ="))); + + expectTokenRequest(); + EXPECT_CALL(mock_encryptor, aes256EncryptWithSizePad(clear_access_token)) + .WillOnce(Return(mb_encrypted_access_token)); + EXPECT_CALL( + mock_ot, + writeFile(encrypted_access_token, data_path + session_token_file_name, false)).WillOnce(Return(true) + ); + EXPECT_CALL(mock_ml, yield(A())).WillOnce( + Invoke( + [] (chrono::microseconds microseconds) + { + EXPECT_EQ(10000000, microseconds.count()); // Validate short expiration time, mininum is 10 sec + throw invalid_argument("stop while loop"); + } + ) + ); + try { + routine(); + } catch (const invalid_argument& e) {} + EXPECT_CALL(mock_ml, doesRoutineExist(1)).WillOnce(Return(true)); + EXPECT_TRUE(authenticateAgent().ok()); +} + +TEST_F(FogCommunicationTest, authenticateAgentFromEnvOTPToken) +{ + EXPECT_CALL(mock_ot, doesFileExist(required_apps_file_path)).WillOnce(Return(false)); + init(); + // Fog ext + setFogExtension("test"); + + char env_token[] = "NANO_AGENT_TOKEN=ThisIsAMochToken"; + putenv(env_token); + + // Reading user cred + Maybe no_cred_err(genError("No Credentials file")); + EXPECT_CALL(mock_ot, readFile(data_path + user_cred_file_name)).WillOnce(Return(no_cred_err)); + + // Reading OTP + EXPECT_CALL(mock_ot, removeFile("/etc/cp/conf/registration-data.json")).WillOnce(Return(true)); + EXPECT_CALL(mock_status, setAgentDetails(agent_id, profile_id, tenant_id)); + EXPECT_CALL(mock_status, + setRegistrationDetails("smartmeter", "Embedded", "gaia", "x86_64")); + EXPECT_CALL( + mock_status, + setFieldStatus(OrchestrationStatusFieldType::REGISTRATION, OrchestrationStatusResult::SUCCESS, "") + ).Times(2); + + Maybe> no_nginx(genError("No nginx")); + EXPECT_CALL(mock_details_resolver, getHostname()).WillOnce(Return(string("smartmeter"))); + EXPECT_CALL(mock_details_resolver, getPlatform()).WillOnce(Return(string("gaia"))); + EXPECT_CALL(mock_details_resolver, getArch()).WillOnce(Return(string("x86_64"))); + EXPECT_CALL(mock_details_resolver, isReverseProxy()).WillOnce(Return(true)); + EXPECT_CALL(mock_details_resolver, isKernelVersion3OrHigher()).WillOnce(Return(true)); + EXPECT_CALL(mock_details_resolver, isGwNotVsx()).WillOnce(Return(true)); + EXPECT_CALL(mock_details_resolver, isVersionEqualOrAboveR8110()).WillOnce(Return(true)); + EXPECT_CALL(mock_details_resolver, parseNginxMetadata()).WillOnce(Return(no_nginx)); + EXPECT_CALL(mock_details_resolver, getResolvedDetails()).WillOnce(Return(map())); + EXPECT_CALL(mock_details_resolver, getAgentVersion()) + .WillOnce(Return(Version::getFullVersion())) + .WillOnce(Return(Version::getFullVersion())); + + expectAuthenticationData( + "{\n" + " \"authenticationData\": [\n" + " {\n" + " \"authenticationMethod\": \"token\",\n" + " \"data\": \"ThisIsAMochToken\"\n" + " }\n" + " ],\n" + " \"metaData\": {\n" + " \"agentName\": \"smartmeter\",\n" + " \"agentType\": \"Embedded\",\n" + " \"platform\": \"gaia\",\n" + " \"architecture\": \"x86_64\",\n" + " \"agentVersion\": \"" + Version::getFullVersion() + "\",\n" + " \"additionalMetaData\": {\n" + " \"agent_version\": \"" + Version::getFullVersion() + "\",\n" + " \"isGwNotVsx\": \"true\",\n" + " \"isKernelVersion3OrHigher\": \"true\",\n" + " \"isVersionEqualOrAboveR8110\": \"true\",\n" + " \"managedMode\": \"management\",\n" + " \"reverse_proxy\": \"true\",\n" + " \"userEdition\": \"PrEm1um%\"\n" + " }\n" + " }\n" + "}" + ); + + // Saving cred + EXPECT_CALL(mock_encryptor, aes256EncryptWithSizePad(clear_cred)).WillOnce(Return(mb_base64_otp)); + EXPECT_CALL(mock_ot, writeFile(base64_otp, data_path + user_cred_file_name, false)).WillOnce(Return(true)); + + // Creating the session token routine + EXPECT_CALL(mock_ml, doesRoutineExist(0)).WillOnce(Return(false)); + I_MainLoop::Routine routine; + EXPECT_CALL(mock_ml, addOneTimeRoutine(I_MainLoop::RoutineType::RealTime, _, _, true)) + .WillOnce(DoAll(SaveArg<1>(&routine), Return(1))); + EXPECT_CALL(mock_ml, yield(chrono::microseconds(11000000))); + EXPECT_FALSE(authenticateAgent().ok()); + + // Looping the routine + EXPECT_CALL(mock_ot, base64Encode("user id:best shared secret")) + .WillOnce(Return(string("dXNlciBpZDpiZXN0IHNoYXJlZCBzZWNyZXQ="))); + + expectTokenRequest(); + EXPECT_CALL(mock_encryptor, aes256EncryptWithSizePad(clear_access_token)) + .WillOnce(Return(mb_encrypted_access_token)); + EXPECT_CALL( + mock_ot, + writeFile(encrypted_access_token, data_path + session_token_file_name, false)).WillOnce(Return(true) + ); + + EXPECT_CALL(mock_ml, yield(A())).WillOnce( + Invoke( + [] (chrono::microseconds microseconds) + { + EXPECT_EQ(10000000, microseconds.count()); // Validate short expiration time, mininum is 10 sec + throw invalid_argument("stop while loop"); + } + ) + ); + try { + routine(); + } catch (const invalid_argument& e) {} + EXPECT_CALL(mock_ml, doesRoutineExist(1)).WillOnce(Return(true)); + EXPECT_TRUE(authenticateAgent().ok()); + unsetenv("NANO_AGENT_TOKEN"); +} + +TEST_F(FogCommunicationTest, registrationWithRequiredApps) +{ + vector intel_file_content({"waap", "accessControl", "ips"}); + CPTestTempfile file(intel_file_content); + setConfiguration(file.fname, "orchestration", "Supported practices file path"); + EXPECT_CALL(mock_ot, doesFileExist(file.fname)).WillOnce(Return(true)); + init(); + // Fog ext + setFogExtension("test"); + + // Reading user cred + Maybe no_cred_err(genError("No Credentials file")); + EXPECT_CALL(mock_ot, readFile(data_path + user_cred_file_name)).WillOnce(Return(no_cred_err)); + + // Reading OTP + EXPECT_CALL(mock_ot, readFile("/etc/cp/conf/registration-data.json")).WillOnce(Return(base64_otp)); + EXPECT_CALL(mock_ot, base64Decode(base64_otp)).WillOnce(Return(clear_otp)); + EXPECT_CALL(mock_ot, removeFile("/etc/cp/conf/registration-data.json")).WillOnce(Return(true)); + EXPECT_CALL(mock_status, setAgentDetails(agent_id, profile_id, tenant_id)); + EXPECT_CALL(mock_status, + setRegistrationDetails("smartmeter", "Embedded", "gaia", "x86_64")); + EXPECT_CALL( + mock_status, + setFieldStatus(OrchestrationStatusFieldType::REGISTRATION, OrchestrationStatusResult::SUCCESS, "") + ).Times(2); + + Maybe> no_nginx(genError("No nginx")); + EXPECT_CALL(mock_details_resolver, getHostname()).WillOnce(Return(string("smartmeter"))); + EXPECT_CALL(mock_details_resolver, getPlatform()).WillOnce(Return(string("gaia"))); + EXPECT_CALL(mock_details_resolver, getArch()).WillOnce(Return(string("x86_64"))); + EXPECT_CALL(mock_details_resolver, isReverseProxy()).WillOnce(Return(true)); + EXPECT_CALL(mock_details_resolver, isKernelVersion3OrHigher()).WillOnce(Return(false)); + EXPECT_CALL(mock_details_resolver, isGwNotVsx()).WillOnce(Return(false)); + EXPECT_CALL(mock_details_resolver, isVersionEqualOrAboveR8110()).WillOnce(Return(false)); + EXPECT_CALL(mock_details_resolver, getResolvedDetails()).WillOnce(Return(map())); + EXPECT_CALL(mock_details_resolver, parseNginxMetadata()).WillOnce(Return(no_nginx)); + EXPECT_CALL(mock_details_resolver, getAgentVersion()) + .WillOnce(Return(Version::getFullVersion())) + .WillOnce(Return(Version::getFullVersion())); + + // Sending cred request + expectAuthenticationData( + "{\n" + " \"authenticationData\": [\n" + " {\n" + " \"authenticationMethod\": \"token\",\n" + " \"data\": \"This is the best OTP token\"\n" + " }\n" + " ],\n" + " \"metaData\": {\n" + " \"agentName\": \"smartmeter\",\n" + " \"agentType\": \"Embedded\",\n" + " \"platform\": \"gaia\",\n" + " \"architecture\": \"x86_64\",\n" + " \"agentVersion\": \"" + Version::getFullVersion() + "\",\n" + " \"additionalMetaData\": {\n" + " \"agent_version\": \"" + Version::getFullVersion() + "\",\n" + " \"managedMode\": \"management\",\n" + " \"require\": \"waap;accessControl;ips\",\n" + " \"reverse_proxy\": \"true\",\n" + " \"userEdition\": \"PrEm1um%\"\n" + " }\n" + " }\n" + "}" + ); + + // Saving cred + EXPECT_CALL(mock_encryptor, aes256EncryptWithSizePad(clear_cred)).WillOnce(Return(mb_base64_otp)); + EXPECT_CALL(mock_ot, writeFile(base64_otp, data_path + user_cred_file_name, false)).WillOnce(Return(true)); + + // Creating the session token routine + EXPECT_CALL(mock_ml, doesRoutineExist(0)).WillOnce(Return(false)); + I_MainLoop::Routine routine; + EXPECT_CALL(mock_ml, addOneTimeRoutine(I_MainLoop::RoutineType::RealTime, _, _, true)) + .WillOnce(DoAll(SaveArg<1>(&routine), Return(1))); + EXPECT_CALL(mock_ml, yield(chrono::microseconds(11000000))); + EXPECT_FALSE(authenticateAgent().ok()); + + // Looping the routine + EXPECT_CALL(mock_ot, base64Encode("user id:best shared secret")) + .WillOnce(Return(string("dXNlciBpZDpiZXN0IHNoYXJlZCBzZWNyZXQ="))); + + expectTokenRequest(); + EXPECT_CALL( + mock_encryptor, + aes256EncryptWithSizePad(clear_access_token) + ).WillOnce(Return(mb_encrypted_access_token)); + + EXPECT_CALL( + mock_ot, + writeFile(encrypted_access_token, data_path + session_token_file_name, false)).WillOnce(Return(true) + ); + EXPECT_CALL(mock_ml, yield(A())).WillOnce( + Invoke( + [] (chrono::microseconds microseconds) + { + EXPECT_EQ(10000000, microseconds.count()); // Validate short expiration time, mininum is 10 sec + throw invalid_argument("stop while loop"); + } + ) + ); + try { + routine(); + } catch (const invalid_argument& e) {} + EXPECT_CALL(mock_ml, doesRoutineExist(1)).WillOnce(Return(true)); + EXPECT_TRUE(authenticateAgent().ok()); +} + +TEST_F(FogCommunicationTest, registrationWithRequiredAppsNginx) +{ + EXPECT_CALL(mock_ot, doesFileExist(required_apps_file_path)).WillOnce(Return(false)); + init(); + // Fog ext + setFogExtension("test"); + + Maybe no_cred_err(genError("No Credentials file")); + EXPECT_CALL(mock_ot, readFile(data_path + user_cred_file_name)).WillOnce(Return(no_cred_err)); + + // Reading OTP + EXPECT_CALL(mock_ot, readFile("/etc/cp/conf/registration-data.json")).WillOnce(Return(base64_otp)); + EXPECT_CALL(mock_ot, base64Decode(base64_otp)).WillOnce(Return(clear_otp)); + EXPECT_CALL(mock_ot, removeFile("/etc/cp/conf/registration-data.json")).WillOnce(Return(true)); + EXPECT_CALL(mock_status, setAgentDetails(agent_id, profile_id, tenant_id)); + EXPECT_CALL(mock_status, + setRegistrationDetails("smartmeter", "Embedded", "gaia", "x86_64")); + EXPECT_CALL( + mock_status, + setFieldStatus(OrchestrationStatusFieldType::REGISTRATION, OrchestrationStatusResult::SUCCESS, "") + ).Times(2); + // Reading agent details for registration + Maybe> nginx_data = make_tuple( + string("--prefix=/etc/nginx --conf=/etc/nginx.conf --log-path=/log/a.log"), + string("-g -O2 -fstack-protecr-strong -Wformat -Werror=format-security"), + string("nginx-1.10.3") + ); + + EXPECT_CALL(mock_details_resolver, getHostname()).WillOnce(Return(string("smartmeter"))); + EXPECT_CALL(mock_details_resolver, getPlatform()).WillOnce(Return(string("gaia"))); + EXPECT_CALL(mock_details_resolver, getArch()).WillOnce(Return(string("x86_64"))); + EXPECT_CALL(mock_details_resolver, isReverseProxy()).WillOnce(Return(true)); + EXPECT_CALL(mock_details_resolver, isKernelVersion3OrHigher()).WillOnce(Return(false)); + EXPECT_CALL(mock_details_resolver, isGwNotVsx()).WillOnce(Return(false)); + EXPECT_CALL(mock_details_resolver, isVersionEqualOrAboveR8110()).WillOnce(Return(false)); + EXPECT_CALL(mock_details_resolver, getResolvedDetails()).WillOnce(Return(map())); + EXPECT_CALL(mock_details_resolver, parseNginxMetadata()).WillOnce(Return(nginx_data)); + EXPECT_CALL(mock_details_resolver, getAgentVersion()) + .WillOnce(Return(Version::getFullVersion())) + .WillOnce(Return(Version::getFullVersion())); + + // Sending cred request + expectAuthenticationData( + "{\n" + " \"authenticationData\": [\n" + " {\n" + " \"authenticationMethod\": \"token\",\n" + " \"data\": \"This is the best OTP token\"\n" + " }\n" + " ],\n" + " \"metaData\": {\n" + " \"agentName\": \"smartmeter\",\n" + " \"agentType\": \"Embedded\",\n" + " \"platform\": \"gaia\",\n" + " \"architecture\": \"x86_64\",\n" + " \"agentVersion\": \"" + Version::getFullVersion() + "\",\n" + " \"additionalMetaData\": {\n" + " \"agent_version\": \"" + Version::getFullVersion() + "\",\n" + " \"configureOpt\": \"--prefix=/etc/nginx --conf=/etc/nginx.conf --log-path=/log/a.log\",\n" + " \"extraCompilerOpt\": \"-g -O2 -fstack-protecr-strong -Wformat -Werror=format-security\",\n" + " \"managedMode\": \"management\",\n" + " \"nginxVersion\": \"nginx-1.10.3\",\n" + " \"reverse_proxy\": \"true\",\n" + " \"userEdition\": \"PrEm1um%\"\n" + " }\n" + " }\n" + "}" + ); + + // Saving cred + EXPECT_CALL(mock_encryptor, aes256EncryptWithSizePad(clear_cred)).WillOnce(Return(mb_base64_otp)); + EXPECT_CALL(mock_ot, writeFile(base64_otp, data_path + user_cred_file_name, false)).WillOnce(Return(true)); + + // Creating the session token routine + EXPECT_CALL(mock_ml, doesRoutineExist(0)).WillOnce(Return(false)); + I_MainLoop::Routine routine; + EXPECT_CALL(mock_ml, addOneTimeRoutine(I_MainLoop::RoutineType::RealTime, _, _, true)) + .WillOnce(DoAll(SaveArg<1>(&routine), Return(1))); + EXPECT_CALL(mock_ml, yield(chrono::microseconds(11000000))); + EXPECT_FALSE(authenticateAgent().ok()); + + // Looping the routine + EXPECT_CALL(mock_ot, base64Encode("user id:best shared secret")) + .WillOnce(Return(string("dXNlciBpZDpiZXN0IHNoYXJlZCBzZWNyZXQ="))); + + expectTokenRequest(); + EXPECT_CALL( + mock_encryptor, + aes256EncryptWithSizePad(clear_access_token) + ).WillOnce(Return(mb_encrypted_access_token)); + + EXPECT_CALL( + mock_ot, + writeFile(encrypted_access_token, data_path + session_token_file_name, false)).WillOnce(Return(true) + ); + EXPECT_CALL(mock_ml, yield(A())).WillOnce( + Invoke( + [] (chrono::microseconds microseconds) + { + EXPECT_EQ(10000000, microseconds.count()); // Validate short expiration time, mininum is 10 sec + throw invalid_argument("stop while loop"); + } + ) + ); + try { + routine(); + } catch (const invalid_argument& e) {} + EXPECT_CALL(mock_ml, doesRoutineExist(1)).WillOnce(Return(true)); + EXPECT_TRUE(authenticateAgent().ok()); +} + +TEST_F(FogCommunicationTest, authenticateAgentFromOTPTokenFailedWriteToFile) +{ + EXPECT_CALL(mock_ot, doesFileExist(required_apps_file_path)).WillOnce(Return(false)); + init(); + // Fog ext + setFogExtension("test"); + + // Reading user cred + Maybe no_cred_err(genError("No Credentials file")); + EXPECT_CALL(mock_ot, readFile(data_path + user_cred_file_name)).WillOnce(Return(no_cred_err)); + + // Reading OTP + EXPECT_CALL(mock_ot, readFile("/etc/cp/conf/registration-data.json")).WillOnce(Return(base64_otp)); + EXPECT_CALL(mock_ot, base64Decode(base64_otp)).WillOnce(Return(clear_otp)); + EXPECT_CALL(mock_ot, removeFile("/etc/cp/conf/registration-data.json")).WillOnce(Return(true)); + EXPECT_CALL(mock_status, setAgentDetails(agent_id, profile_id, tenant_id)); + EXPECT_CALL(mock_status, + setRegistrationDetails("smartmeter", "Embedded", "gaia", "x86_64")); + EXPECT_CALL( + mock_status, + setFieldStatus(OrchestrationStatusFieldType::REGISTRATION, OrchestrationStatusResult::SUCCESS, "") + ).Times(2); + + Maybe> no_nginx(genError("No nginx")); + EXPECT_CALL(mock_details_resolver, getHostname()).WillOnce(Return(string("smartmeter"))); + EXPECT_CALL(mock_details_resolver, getPlatform()).WillOnce(Return(string("gaia"))); + EXPECT_CALL(mock_details_resolver, getArch()).WillOnce(Return(string("x86_64"))); + EXPECT_CALL(mock_details_resolver, isReverseProxy()).WillOnce(Return(true)); + EXPECT_CALL(mock_details_resolver, isKernelVersion3OrHigher()).WillOnce(Return(false)); + EXPECT_CALL(mock_details_resolver, isGwNotVsx()).WillOnce(Return(false)); + EXPECT_CALL(mock_details_resolver, isVersionEqualOrAboveR8110()).WillOnce(Return(false)); + EXPECT_CALL(mock_details_resolver, getResolvedDetails()).WillOnce(Return(map())); + EXPECT_CALL(mock_details_resolver, parseNginxMetadata()).WillOnce(Return(no_nginx)); + EXPECT_CALL(mock_details_resolver, getAgentVersion()) + .WillOnce(Return(Version::getFullVersion())) + .WillOnce(Return(Version::getFullVersion())); + + // Sending cred request + expectAuthenticationData( + "{\n" + " \"authenticationData\": [\n" + " {\n" + " \"authenticationMethod\": \"token\",\n" + " \"data\": \"This is the best OTP token\"\n" + " }\n" + " ],\n" + " \"metaData\": {\n" + " \"agentName\": \"smartmeter\",\n" + " \"agentType\": \"Embedded\",\n" + " \"platform\": \"gaia\",\n" + " \"architecture\": \"x86_64\",\n" + " \"agentVersion\": \"" + Version::getFullVersion() + "\",\n" + " \"additionalMetaData\": {\n" + " \"agent_version\": \"" + Version::getFullVersion() + "\",\n" + " \"managedMode\": \"management\",\n" + " \"reverse_proxy\": \"true\",\n" + " \"userEdition\": \"PrEm1um%\"\n" + " }\n" + " }\n" + "}" + ); + + // Saving cred +#ifndef DISABLE_APPSEC_DATA_ENCRYPTION + EXPECT_CALL(mock_encryptor, aes256EncryptWithSizePad(clear_cred)).Times(2).WillRepeatedly(Return(mb_base64_otp)); +#endif // DISABLE_APPSEC_DATA_ENCRYPTION + I_MainLoop::Routine rewrite_routine; + EXPECT_CALL(mock_ml, addOneTimeRoutine(I_MainLoop::RoutineType::Offline, _, _, false)) + .WillOnce(DoAll(SaveArg<1>(&rewrite_routine), Return(1))); +#ifndef DISABLE_APPSEC_DATA_ENCRYPTION + EXPECT_CALL(mock_ot, writeFile(base64_otp, data_path + user_cred_file_name, false)) + .WillOnce(Return(false)) // Will retry after 1 min + .WillOnce(Return(true)); +#endif // DISABLE_APPSEC_DATA_ENCRYPTION +#ifdef DISABLE_APPSEC_DATA_ENCRYPTION + EXPECT_CALL(mock_ot, writeFile(clear_cred, data_path + user_cred_file_name, false)) + .WillOnce(Return(false)) // Will retry after 1 min + .WillOnce(Return(true)); +#endif // DISABLE_APPSEC_DATA_ENCRYPTION + + // Creating the session token routine + EXPECT_CALL(mock_ml, doesRoutineExist(0)).WillOnce(Return(false)); + I_MainLoop::Routine routine; + EXPECT_CALL(mock_ml, addOneTimeRoutine(I_MainLoop::RoutineType::RealTime, _, _, true)) + .WillOnce(DoAll(SaveArg<1>(&routine), Return(1))); + EXPECT_CALL(mock_ml, yield(chrono::microseconds(11000000))); + EXPECT_FALSE(authenticateAgent().ok()); + + // Looping the routine + EXPECT_CALL(mock_ot, base64Encode("user id:best shared secret")) + .WillOnce(Return(string("dXNlciBpZDpiZXN0IHNoYXJlZCBzZWNyZXQ="))); + expectTokenRequest(); + +#ifndef DISABLE_APPSEC_DATA_ENCRYPTION + EXPECT_CALL( + mock_encryptor, + aes256EncryptWithSizePad(clear_access_token) + ).WillOnce(Return(mb_encrypted_access_token)); + EXPECT_CALL( + mock_ot, + writeFile(encrypted_access_token, data_path + session_token_file_name, false)).WillOnce(Return(true) + ); +#endif // DISABLE_APPSEC_DATA_ENCRYPTION + +#ifdef DISABLE_APPSEC_DATA_ENCRYPTION + EXPECT_CALL( + mock_ot, + writeFile(clear_access_token, data_path + session_token_file_name, false)).WillOnce(Return(true) + ); +#endif // DISABLE_APPSEC_DATA_ENCRYPTION + + EXPECT_CALL(mock_ml, yield(A())) + .WillOnce( + Invoke( + [] (chrono::microseconds microseconds) + { + EXPECT_EQ(10000000, microseconds.count()); // Validate short expiration time, mininum is 10 sec + throw invalid_argument("stop while loop"); + } + ) + ); + try { + routine(); + } catch (const invalid_argument& e) { + try { + rewrite_routine(); + } catch (const invalid_argument& e) {} + } + EXPECT_CALL(mock_ml, doesRoutineExist(1)).WillOnce(Return(true)); + EXPECT_TRUE(authenticateAgent().ok()); + + string obfuscatedToken = "102123021002132312312312312"; + EXPECT_CALL(mock_encryptor, obfuscateXorBase64("BEST ACCESS TOKEN EVER")).WillOnce(Return(obfuscatedToken)); + stringstream is; + is << "{}"; + auto output = rest_handler->performRestCall(is); + + string res = + "{\n" + " \"token\": \"" + obfuscatedToken + "\",\n" + " \"expiration\": 100\n" + "}"; + EXPECT_THAT(output, IsValue(res)); +} + +TEST_F(FogCommunicationTest, invalidCheckUpdate) +{ + EXPECT_CALL(mock_ot, doesFileExist(required_apps_file_path)).WillOnce(Return(false)); + init(); + CheckUpdateRequest req("", "", "", "", I_OrchestrationTools::SELECTED_CHECKSUM_TYPE_STR, "0"); + EXPECT_THAT(checkUpdate(req), IsError("Acccess Token not available.")); +} + +TEST_F(FogCommunicationTest, checkUpdate) +{ + setSetting("scheduled", "upgradeMode"); + setSetting("13:00", "upgradeTime"); + EXPECT_CALL(mock_ot, doesFileExist(required_apps_file_path)).WillOnce(Return(false)); + init(); + // Reading user cred + Maybe no_cred_err(genError("No Credentials file")); + EXPECT_CALL(mock_ot, readFile(data_path + user_cred_file_name)).WillOnce(Return(no_cred_err)); + + // Reading OTP + EXPECT_CALL(mock_ot, readFile("/etc/cp/conf/registration-data.json")).WillOnce(Return(base64_otp)); + EXPECT_CALL(mock_ot, base64Decode(base64_otp)).WillOnce(Return(clear_otp)); + EXPECT_CALL(mock_ot, removeFile("/etc/cp/conf/registration-data.json")).WillOnce(Return(true)); + + // Reading agent details for registration + Maybe> no_nginx(genError("No nginx")); + EXPECT_CALL(mock_details_resolver, getHostname()).WillOnce(Return(string("smartmeter"))); + EXPECT_CALL(mock_details_resolver, getPlatform()).WillOnce(Return(string("gaia"))); + EXPECT_CALL(mock_details_resolver, getArch()).WillOnce(Return(string("x86_64"))); + EXPECT_CALL(mock_details_resolver, isReverseProxy()).WillOnce(Return(true)); + EXPECT_CALL(mock_details_resolver, isKernelVersion3OrHigher()).WillOnce(Return(false)); + EXPECT_CALL(mock_details_resolver, isGwNotVsx()).WillOnce(Return(false)); + EXPECT_CALL(mock_details_resolver, isVersionEqualOrAboveR8110()).WillOnce(Return(false)); + EXPECT_CALL(mock_details_resolver, getResolvedDetails()).WillOnce(Return(map())); + EXPECT_CALL(mock_details_resolver, parseNginxMetadata()).WillOnce(Return(no_nginx)); + EXPECT_CALL(mock_details_resolver, getAgentVersion()) + .WillOnce(Return(Version::getFullVersion())) + .WillOnce(Return(Version::getFullVersion())); + + EXPECT_CALL(mock_status, setAgentDetails(agent_id, profile_id, tenant_id)); + EXPECT_CALL(mock_status, setRegistrationDetails("smartmeter", "Embedded", "gaia", "x86_64")); + EXPECT_CALL( + mock_status, + setFieldStatus(OrchestrationStatusFieldType::REGISTRATION, OrchestrationStatusResult::SUCCESS, "") + ).Times(2); + + // Sending cred request + expectAuthenticationData( + "{\n" + " \"authenticationData\": [\n" + " {\n" + " \"authenticationMethod\": \"token\",\n" + " \"data\": \"This is the best OTP token\"\n" + " }\n" + " ],\n" + " \"metaData\": {\n" + " \"agentName\": \"smartmeter\",\n" + " \"agentType\": \"Embedded\",\n" + " \"platform\": \"gaia\",\n" + " \"architecture\": \"x86_64\",\n" + " \"agentVersion\": \"" + Version::getFullVersion() + "\",\n" + " \"additionalMetaData\": {\n" + " \"agent_version\": \"" + Version::getFullVersion() + "\",\n" + " \"managedMode\": \"management\",\n" + " \"reverse_proxy\": \"true\",\n" + " \"userEdition\": \"PrEm1um%\"\n" + " }\n" + " }\n" + "}" + ); + + // Saving cred + EXPECT_CALL(mock_encryptor, aes256EncryptWithSizePad(clear_cred)).WillOnce(Return(mb_base64_otp)); + EXPECT_CALL(mock_ot, writeFile(base64_otp, data_path + user_cred_file_name, false)).WillOnce(Return(true)); + + // Creating the session token routine + EXPECT_CALL(mock_ml, doesRoutineExist(0)).WillOnce(Return(false)); + I_MainLoop::Routine routine; + EXPECT_CALL(mock_ml, addOneTimeRoutine(I_MainLoop::RoutineType::RealTime, _, _, true)) + .WillOnce(DoAll(SaveArg<1>(&routine), Return(1))); + EXPECT_CALL(mock_ml, yield(chrono::microseconds(11000000))); + EXPECT_FALSE(authenticateAgent().ok()); + + // Looping the routine + EXPECT_CALL(mock_ot, base64Encode("user id:best shared secret")) + .WillOnce(Return(string("dXNlciBpZDpiZXN0IHNoYXJlZCBzZWNyZXQ="))); + + expectTokenRequest(); + EXPECT_CALL(mock_encryptor, aes256EncryptWithSizePad(clear_access_token)) + .WillOnce(Return(mb_encrypted_access_token)); + EXPECT_CALL( + mock_ot, + writeFile(encrypted_access_token, data_path + session_token_file_name, false)).WillOnce(Return(true) + ); + + EXPECT_CALL(mock_ml, yield(A())).WillOnce( + Invoke( + [] (chrono::microseconds microseconds) + { + EXPECT_EQ(10000000, microseconds.count()); // Validate short expiration time, mininum is 10 sec + throw invalid_argument("stop while loop"); + } + ) + ); + try { + routine(); + } catch (const invalid_argument& e) {} + EXPECT_CALL(mock_ml, doesRoutineExist(1)).WillOnce(Return(true)); + EXPECT_TRUE(authenticateAgent().ok()); + + // Sending checkupdate request + expectCheckupdateRequest( + "{\n" + " \"manifest\": \"\",\n" + " \"policy\": \"\",\n" + " \"settings\": \"\",\n" + " \"data\": \"\",\n" + " \"virtualSettings\": {\n" + " \"tenants\": []\n" + " },\n" + " \"virtualPolicy\": {\n" + " \"tenants\": []\n" + " },\n" + " \"checksum-type\": \"sha256sum\",\n" + " \"policyVersion\": \"12\",\n" + " \"localConfigurationSettings\": {\n" + " \"upgradeSchedule\": {\n" + " \"upgradeMode\": \"scheduled\",\n" + " \"upgradeTime\": \"13:00\",\n" + " \"upgradeDurationHours\": 4\n" + " }\n" + " }\n" + "}", + "{" + " \"manifest\" : \"A\"," + " \"policy\" : \"B\"," + " \"settings\" : \"C\"," + " \"data\" : \"D\"" + "}" + ); + CheckUpdateRequest req("", "", "", "", I_OrchestrationTools::SELECTED_CHECKSUM_TYPE_STR, "12"); + setUpgradeFields(req); + auto response = checkUpdate(req); + EXPECT_TRUE(response.ok()); + + EXPECT_THAT(req.getManifest(), IsValue("A")); + EXPECT_THAT(req.getPolicy(), IsValue("B")); + EXPECT_THAT(req.getSettings(), IsValue("C")); + EXPECT_THAT(req.getData(), IsValue("D")); +} + +TEST_F(FogCommunicationTest, checkUpdateDeclarativeMode) +{ + EXPECT_CALL(mock_env_details, getEnvType()).WillRepeatedly(Return(EnvType::LINUX)); + + setSetting("declarative", "profileManagedMode"); + setSetting("scheduled", "upgradeMode"); + setSetting("13:00", "upgradeTime"); + setSetting(6, "upgradeDurationHours"); + setSetting>({"Sunday", "Monday"}, "upgradeDay"); + Maybe checksum_value(string("12345")); + EXPECT_CALL(mock_ot, calculateChecksum( + I_OrchestrationTools::SELECTED_CHECKSUM_TYPE, + _)).WillRepeatedly(Return(checksum_value)); + EXPECT_CALL(mock_shell_cmd, getExecOutput(_, _, _)).WillRepeatedly(Return(checksum_value)); + + string policy_path = "/etc/cp/conf/local_policy.yaml"; + stringstream is; + is << "{\"policy_path\": \""+ policy_path +"\"}"; + auto output = apply_policy->performRestCall(is); + + EXPECT_CALL(mock_ot, doesFileExist(required_apps_file_path)).WillOnce(Return(false)); + init(); + // Reading user cred + Maybe no_cred_err(genError("No Credentials file")); + EXPECT_CALL(mock_ot, readFile(data_path + user_cred_file_name)).WillOnce(Return(no_cred_err)); + + // Reading OTP + EXPECT_CALL(mock_ot, readFile("/etc/cp/conf/registration-data.json")).WillOnce(Return(base64_otp)); + EXPECT_CALL(mock_ot, base64Decode(base64_otp)).WillOnce(Return(clear_otp)); + EXPECT_CALL(mock_ot, removeFile("/etc/cp/conf/registration-data.json")).WillOnce(Return(true)); + + // Reading agent details for registration + Maybe> no_nginx(genError("No nginx")); + EXPECT_CALL(mock_details_resolver, getHostname()).WillOnce(Return(string("smartmeter"))); + EXPECT_CALL(mock_details_resolver, getPlatform()).WillOnce(Return(string("gaia"))); + EXPECT_CALL(mock_details_resolver, getArch()).WillOnce(Return(string("x86_64"))); + EXPECT_CALL(mock_details_resolver, isReverseProxy()).WillOnce(Return(true)); + EXPECT_CALL(mock_details_resolver, isKernelVersion3OrHigher()).WillOnce(Return(false)); + EXPECT_CALL(mock_details_resolver, isGwNotVsx()).WillOnce(Return(false)); + EXPECT_CALL(mock_details_resolver, isVersionEqualOrAboveR8110()).WillOnce(Return(false)); + EXPECT_CALL(mock_details_resolver, getResolvedDetails()).WillOnce(Return(map())); + EXPECT_CALL(mock_details_resolver, parseNginxMetadata()).WillOnce(Return(no_nginx)); + EXPECT_CALL(mock_details_resolver, getAgentVersion()) + .WillOnce(Return(Version::getFullVersion())) + .WillOnce(Return(Version::getFullVersion())); + + EXPECT_CALL(mock_status, setAgentDetails(agent_id, profile_id, tenant_id)); + EXPECT_CALL(mock_status, + setRegistrationDetails("smartmeter", "Embedded", "gaia", "x86_64")); + EXPECT_CALL( + mock_status, + setFieldStatus(OrchestrationStatusFieldType::REGISTRATION, OrchestrationStatusResult::SUCCESS, "") + ).Times(2); + + // Sending cred request + expectAuthenticationData( + "{\n" + " \"authenticationData\": [\n" + " {\n" + " \"authenticationMethod\": \"token\",\n" + " \"data\": \"This is the best OTP token\"\n" + " }\n" + " ],\n" + " \"metaData\": {\n" + " \"agentName\": \"smartmeter\",\n" + " \"agentType\": \"Embedded\",\n" + " \"platform\": \"gaia\",\n" + " \"architecture\": \"x86_64\",\n" + " \"agentVersion\": \"" + Version::getFullVersion() + "\",\n" + " \"additionalMetaData\": {\n" + " \"agent_version\": \"" + Version::getFullVersion() + "\",\n" + " \"managedMode\": \"declarative\",\n" + " \"reverse_proxy\": \"true\",\n" + " \"userEdition\": \"PrEm1um%\"\n" + " }\n" + " }\n" + "}" + ); + + // Saving cred + EXPECT_CALL(mock_encryptor, aes256EncryptWithSizePad(clear_cred)).WillOnce(Return(mb_base64_otp)); + EXPECT_CALL(mock_ot, writeFile(base64_otp, data_path + user_cred_file_name, false)).WillOnce(Return(true)); + + // Creating the session token routine + EXPECT_CALL(mock_ml, doesRoutineExist(0)).WillOnce(Return(false)); + I_MainLoop::Routine routine; + EXPECT_CALL(mock_ml, addOneTimeRoutine(I_MainLoop::RoutineType::RealTime, _, _, true)) + .WillOnce(DoAll(SaveArg<1>(&routine), Return(1))); + EXPECT_CALL(mock_ml, yield(chrono::microseconds(11000000))); + EXPECT_FALSE(authenticateAgent().ok()); + + // Looping the routine + EXPECT_CALL(mock_ot, base64Encode("user id:best shared secret")) + .WillOnce(Return(string("dXNlciBpZDpiZXN0IHNoYXJlZCBzZWNyZXQ="))); + + expectTokenRequest(); + EXPECT_CALL( + mock_encryptor, + aes256EncryptWithSizePad(clear_access_token) + ).WillOnce(Return(mb_encrypted_access_token)); + + EXPECT_CALL( + mock_ot, + writeFile(encrypted_access_token, data_path + session_token_file_name, false)).WillOnce(Return(true) + ); + EXPECT_CALL(mock_ml, yield(A())).WillOnce( + Invoke( + [] (chrono::microseconds microseconds) + { + EXPECT_EQ(10000000, microseconds.count()); // Validate short expiration time, mininum is 10 sec + throw invalid_argument("stop while loop"); + } + ) + ); + try { + routine(); + } catch (const invalid_argument& e) {} + EXPECT_CALL(mock_ml, doesRoutineExist(1)).WillOnce(Return(true)); + EXPECT_TRUE(authenticateAgent().ok()); + + // Sending checkupdate request + expectCheckupdateRequest( + "{\n" + " \"manifest\": \"\",\n" + " \"policy\": \"\",\n" + " \"settings\": \"\",\n" + " \"data\": \"\",\n" + " \"virtualSettings\": {\n" + " \"tenants\": []\n" + " },\n" + " \"virtualPolicy\": {\n" + " \"tenants\": []\n" + " },\n" + " \"checksum-type\": \"sha256sum\",\n" + " \"policyVersion\": \"12\",\n" + " \"localConfigurationSettings\": {\n" + " \"upgradeSchedule\": {\n" + " \"upgradeMode\": \"scheduled\",\n" + " \"upgradeTime\": \"13:00\",\n" + " \"upgradeDurationHours\": 6,\n" + " \"upgradeDay\": [\n" + " \"Sunday\",\n" + " \"Monday\"\n" + " ]\n" + " }\n" + " }\n" + "}", + "{" + " \"manifest\" : \"A\"," + " \"policy\" : \"B\"," + " \"settings\" : \"C\"," + " \"data\" : \"D\"" + "}" + ); + CheckUpdateRequest req("", "", "", "", I_OrchestrationTools::SELECTED_CHECKSUM_TYPE_STR, "12"); + setUpgradeFields(req); + auto response = checkUpdate(req); + EXPECT_TRUE(response.ok()); + + EXPECT_THAT(req.getManifest(), IsValue("A")); + EXPECT_THAT(req.getPolicy(), IsValue("12345")); + EXPECT_THAT(req.getSettings(), IsValue("C")); + EXPECT_THAT(req.getData(), IsValue("D")); +} + +TEST_F(FogCommunicationTest, emptyCheckUpdate) +{ + setSetting("scheduled", "upgradeMode"); + setSetting("13:00", "upgradeTime"); + setSetting(6, "upgradeDurationHours"); + EXPECT_CALL(mock_ot, doesFileExist(required_apps_file_path)).WillOnce(Return(false)); + init(); + // Reading user cred + EXPECT_CALL(mock_ot, readFile(data_path + user_cred_file_name)).WillOnce(Return(encrypted_cred)); + EXPECT_CALL(mock_encryptor, aes256DecryptWithSizePad(encrypted_cred)).WillOnce(Return(clear_cred)); + EXPECT_CALL( + mock_status, + setFieldStatus(OrchestrationStatusFieldType::REGISTRATION, OrchestrationStatusResult::SUCCESS, "") + ).Times(2); + // Creating the session token routine + EXPECT_CALL(mock_ml, doesRoutineExist(0)).WillOnce(Return(false)); + I_MainLoop::Routine routine; + EXPECT_CALL(mock_ml, addOneTimeRoutine(I_MainLoop::RoutineType::RealTime, _, _, true)) + .WillOnce(DoAll(SaveArg<1>(&routine), Return(1))); + EXPECT_CALL(mock_ml, yield(chrono::microseconds(11000000))); + EXPECT_FALSE(authenticateAgent().ok()); + + // Looping og the routine + EXPECT_CALL(mock_ot, base64Encode("user id:best shared secret")) + .WillOnce(Return(string("dXNlciBpZDpiZXN0IHNoYXJlZCBzZWNyZXQ="))); + + expectTokenRequest(); + EXPECT_CALL( + mock_encryptor, + aes256EncryptWithSizePad(clear_access_token) + ).WillOnce(Return(mb_encrypted_access_token)); + EXPECT_CALL( + mock_ot, + writeFile(encrypted_access_token, data_path + session_token_file_name, false)).WillOnce(Return(true) + ); + EXPECT_CALL(mock_ml, yield(A())).WillOnce( + Invoke( + [] (chrono::microseconds microseconds) + { + EXPECT_EQ(10000000, microseconds.count()); // Validate short expiration time, mininum is 10 sec + throw invalid_argument("stop while loop"); + } + ) + ); + try { + routine(); + } catch (const invalid_argument& e) {} + EXPECT_CALL(mock_ml, doesRoutineExist(1)).WillOnce(Return(true)); + EXPECT_TRUE(authenticateAgent().ok()); + + // Sending checkupdate request + expectCheckupdateRequest( + "{\n" + " \"manifest\": \"A\",\n" + " \"policy\": \"B\",\n" + " \"settings\": \"C\",\n" + " \"data\": \"D\",\n" + " \"virtualSettings\": {\n" + " \"tenants\": []\n" + " },\n" + " \"virtualPolicy\": {\n" + " \"tenants\": []\n" + " },\n" + " \"checksum-type\": \"sha256sum\",\n" + " \"policyVersion\": \"12\",\n" + " \"localConfigurationSettings\": {\n" + " \"upgradeSchedule\": {\n" + " \"upgradeMode\": \"scheduled\",\n" + " \"upgradeTime\": \"13:00\",\n" + " \"upgradeDurationHours\": 6\n" + " }\n" + " }\n" + "}", + "{" + " \"manifest\" : \"\"," + " \"policy\" : \"\"," + " \"settings\" : \"\"," + " \"data\" : \"\"" + "}" + ); + + CheckUpdateRequest req("A", "B", "C", "D", I_OrchestrationTools::SELECTED_CHECKSUM_TYPE_STR, "12"); + setUpgradeFields(req); + auto response = checkUpdate(req); + EXPECT_TRUE(response.ok()); + + EXPECT_THAT(req.getManifest(), IsError("No manifest")); + EXPECT_THAT(req.getPolicy(), IsError("No policy")); + EXPECT_THAT(req.getSettings(), IsError("No settings")); +} + +TEST_F(FogCommunicationTest, downloadFile) +{ + EXPECT_CALL(mock_ot, doesFileExist(required_apps_file_path)).WillOnce(Return(false)); + init(); + // Reading user cred + EXPECT_CALL(mock_ot, readFile(data_path + user_cred_file_name)).WillOnce(Return(encrypted_cred)); + EXPECT_CALL(mock_encryptor, aes256DecryptWithSizePad(encrypted_cred)).WillOnce(Return(clear_cred)); + EXPECT_CALL( + mock_status, + setFieldStatus(OrchestrationStatusFieldType::REGISTRATION, OrchestrationStatusResult::SUCCESS, "") + ).Times(2); + // Creating the session token routine + EXPECT_CALL(mock_ml, doesRoutineExist(0)).WillOnce(Return(false)); + I_MainLoop::Routine routine; + EXPECT_CALL(mock_ml, addOneTimeRoutine(I_MainLoop::RoutineType::RealTime, _, _, true)) + .WillOnce(DoAll(SaveArg<1>(&routine), Return(1))); + EXPECT_CALL(mock_ml, yield(chrono::microseconds(11000000))); + EXPECT_FALSE(authenticateAgent().ok()); + + // Looping og the routine + EXPECT_CALL(mock_ot, base64Encode("user id:best shared secret")) + .WillOnce(Return(string("dXNlciBpZDpiZXN0IHNoYXJlZCBzZWNyZXQ="))); + + expectTokenRequest(); + EXPECT_CALL( + mock_encryptor, + aes256EncryptWithSizePad(clear_access_token) + ).WillOnce(Return(mb_encrypted_access_token)); + EXPECT_CALL( + mock_ot, + writeFile(encrypted_access_token, data_path + session_token_file_name, false)).WillOnce(Return(true) + ); + EXPECT_CALL(mock_ml, yield(A())).WillOnce( + Invoke( + [] (chrono::microseconds microseconds) + { + EXPECT_EQ(10000000, microseconds.count()); // Validate short expiration time, mininum is 10 sec + throw invalid_argument("stop while loop"); + } + ) + ); + try { + routine(); + } catch (const invalid_argument& e) {} + EXPECT_CALL(mock_ml, doesRoutineExist(1)).WillOnce(Return(true)); + EXPECT_TRUE(authenticateAgent().ok()); + + // Downloading file + string response = "Best Attribute file"; + EXPECT_CALL( + mock_message, + downloadFile( + HTTPMethod::GET, + "/api/v2/agents/resources/manifest", + "/tmp/orch_files/", + _, + _ + ) + ).WillOnce(Return(Maybe(HTTPStatusCode::HTTP_OK))); + + GetResourceFile manifest_file(GetResourceFile::ResourceFileType::MANIFEST); + EXPECT_THAT(downloadAttributeFile(manifest_file, "/tmp/orch_files/"), IsValue("/tmp/orch_files/")); +} + +TEST_F(FogCommunicationTest, downloadFileDeclarativeMode) +{ + setSetting("declarative", "profileManagedMode"); + Maybe checksum_value(string("12345")); + EXPECT_CALL(mock_ot, calculateChecksum( + I_OrchestrationTools::SELECTED_CHECKSUM_TYPE, + _)).WillRepeatedly(Return(checksum_value)); + EXPECT_CALL(mock_shell_cmd, getExecOutput(_, _, _)).WillRepeatedly(Return(checksum_value)); + + ApplyPolicyEvent apply_policy_event; + apply_policy_event.notify(); + + EXPECT_CALL(mock_ot, doesFileExist(required_apps_file_path)).WillOnce(Return(false)); + init(); + // Reading user cred + EXPECT_CALL(mock_ot, readFile(data_path + user_cred_file_name)).WillOnce(Return(encrypted_cred)); + EXPECT_CALL(mock_encryptor, aes256DecryptWithSizePad(encrypted_cred)).WillOnce(Return(clear_cred)); + EXPECT_CALL( + mock_status, + setFieldStatus(OrchestrationStatusFieldType::REGISTRATION, OrchestrationStatusResult::SUCCESS, "") + ).Times(2); + // Creating the session token routine + EXPECT_CALL(mock_ml, doesRoutineExist(0)).WillOnce(Return(false)); + I_MainLoop::Routine routine; + EXPECT_CALL(mock_ml, addOneTimeRoutine(I_MainLoop::RoutineType::RealTime, _, _, true)) + .WillOnce(DoAll(SaveArg<1>(&routine), Return(1))); + EXPECT_CALL(mock_ml, yield(chrono::microseconds(11000000))); + EXPECT_FALSE(authenticateAgent().ok()); + + // Looping og the routine + EXPECT_CALL(mock_ot, base64Encode("user id:best shared secret")) + .WillOnce(Return(string("dXNlciBpZDpiZXN0IHNoYXJlZCBzZWNyZXQ="))); + + expectTokenRequest(); + EXPECT_CALL( + mock_encryptor, + aes256EncryptWithSizePad(clear_access_token) + ).WillOnce(Return(mb_encrypted_access_token)); + EXPECT_CALL( + mock_ot, + writeFile(encrypted_access_token, data_path + session_token_file_name, false)).WillOnce(Return(true) + ); + EXPECT_CALL(mock_ml, yield(A())).WillOnce( + Invoke( + [] (chrono::microseconds microseconds) + { + EXPECT_EQ(10000000, microseconds.count()); // Validate short expiration time, mininum is 10 sec + throw invalid_argument("stop while loop"); + } + ) + ); + try { + routine(); + } catch (const invalid_argument& e) {} + EXPECT_CALL(mock_ml, doesRoutineExist(1)).WillOnce(Return(true)); + EXPECT_TRUE(authenticateAgent().ok()); + + // Downloading file + GetResourceFile policy_file(GetResourceFile::ResourceFileType::POLICY); + EXPECT_THAT(downloadAttributeFile(policy_file, "/tmp/orch_files/"), IsValue("")); +} + +TEST_F(FogCommunicationTest, changeRenewToken) +{ + setSetting("automatic", "upgradeMode"); + EXPECT_CALL(mock_ot, doesFileExist(required_apps_file_path)).WillOnce(Return(false)); + init(); + + preload(); + + // Set new configuration + setConfiguration(100, "fog communication", "Time (seconds) to renew token prior its expiration"); + + // Reading user cred + Maybe no_cred_err(genError("No Credentials file")); + EXPECT_CALL(mock_ot, readFile(data_path + user_cred_file_name)).WillOnce(Return(no_cred_err)); + EXPECT_CALL(mock_status, setAgentDetails(agent_id, profile_id, tenant_id)); + EXPECT_CALL( + mock_status, + setRegistrationDetails("smartmeter", "Embedded", "linux", "x86_64") + ); + EXPECT_CALL( + mock_status, + setFieldStatus(OrchestrationStatusFieldType::REGISTRATION, OrchestrationStatusResult::SUCCESS, "") + ).Times(2); + // Reading OTP + EXPECT_CALL(mock_ot, readFile("/etc/cp/conf/registration-data.json")).WillOnce(Return(base64_otp)); + EXPECT_CALL(mock_ot, base64Decode(base64_otp)).WillOnce(Return(clear_otp)); + EXPECT_CALL(mock_ot, removeFile("/etc/cp/conf/registration-data.json")).WillOnce(Return(true)); + + // Reading agent details for registration + Maybe> no_nginx(genError("No nginx")); + EXPECT_CALL(mock_details_resolver, getHostname()).WillOnce(Return(string("smartmeter"))); + EXPECT_CALL(mock_details_resolver, getPlatform()).WillOnce(Return(string("linux"))); + EXPECT_CALL(mock_details_resolver, getArch()).WillOnce(Return(string("x86_64"))); + EXPECT_CALL(mock_details_resolver, isReverseProxy()).WillOnce(Return(false)); + EXPECT_CALL(mock_details_resolver, isKernelVersion3OrHigher()).WillOnce(Return(false)); + EXPECT_CALL(mock_details_resolver, isGwNotVsx()).WillOnce(Return(false)); + EXPECT_CALL(mock_details_resolver, isVersionEqualOrAboveR8110()).WillOnce(Return(false)); + EXPECT_CALL(mock_details_resolver, getResolvedDetails()).WillOnce(Return(map())); + EXPECT_CALL(mock_details_resolver, parseNginxMetadata()).WillOnce(Return(no_nginx)); + EXPECT_CALL(mock_details_resolver, getAgentVersion()) + .WillOnce(Return(Version::getFullVersion())) + .WillOnce(Return(Version::getFullVersion())); + + // Sending cred request + expectAuthenticationData( + "{\n" + " \"authenticationData\": [\n" + " {\n" + " \"authenticationMethod\": \"token\",\n" + " \"data\": \"This is the best OTP token\"\n" + " }\n" + " ],\n" + " \"metaData\": {\n" + " \"agentName\": \"smartmeter\",\n" + " \"agentType\": \"Embedded\",\n" + " \"platform\": \"linux\",\n" + " \"architecture\": \"x86_64\",\n" + " \"agentVersion\": \"" + Version::getFullVersion() + "\",\n" + " \"additionalMetaData\": {\n" + " \"agent_version\": \"" + Version::getFullVersion() + "\",\n" + " \"managedMode\": \"management\",\n" + " \"userEdition\": \"PrEm1um%\"\n" + " }\n" + " }\n" + "}" + ); + + // Saving cred + EXPECT_CALL(mock_encryptor, aes256EncryptWithSizePad(clear_cred)).WillOnce(Return(mb_base64_otp)); + EXPECT_CALL(mock_ot, writeFile(base64_otp, data_path + user_cred_file_name, false)).WillOnce(Return(true)); + + // Creating the session token routine + EXPECT_CALL(mock_ml, doesRoutineExist(0)).WillOnce(Return(false)); + I_MainLoop::Routine routine; + EXPECT_CALL(mock_ml, addOneTimeRoutine(I_MainLoop::RoutineType::RealTime, _, _, true)) + .WillOnce(DoAll(SaveArg<1>(&routine), Return(1))); + EXPECT_CALL(mock_ml, yield(chrono::microseconds(11000000))); + EXPECT_FALSE(authenticateAgent().ok()); + // Looping the routine + EXPECT_CALL(mock_ot, base64Encode("user id:best shared secret")) + .WillOnce(Return(string("dXNlciBpZDpiZXN0IHNoYXJlZCBzZWNyZXQ="))); + + expectTokenRequest(); + EXPECT_CALL(mock_encryptor, aes256EncryptWithSizePad(clear_access_token)) + .WillOnce(Return(mb_encrypted_access_token)); + EXPECT_CALL( + mock_ot, + writeFile(encrypted_access_token, data_path + session_token_file_name, false)).WillOnce(Return(true) + ); + EXPECT_CALL(mock_ml, yield(A())).WillOnce( + Invoke( + [] (chrono::microseconds microseconds) + { + EXPECT_EQ(10000000, microseconds.count()); // Validate short expiration time, mininum is 10 sec + throw invalid_argument("stop while loop"); + } + ) + ); + + // Running the routine + try { + routine(); + } catch (const invalid_argument& e) {} + EXPECT_CALL(mock_ml, doesRoutineExist(1)).WillOnce(Return(true)); + EXPECT_TRUE(authenticateAgent().ok()); + + // Sending checkupdate request + expectCheckupdateRequest( + "{\n" + " \"manifest\": \"\",\n" + " \"policy\": \"\",\n" + " \"settings\": \"\",\n" + " \"data\": \"\",\n" + " \"virtualSettings\": {\n" + " \"tenants\": []\n" + " },\n" + " \"virtualPolicy\": {\n" + " \"tenants\": []\n" + " },\n" + " \"checksum-type\": \"sha256sum\",\n" + " \"policyVersion\": \"12\",\n" + " \"localConfigurationSettings\": {\n" + " \"upgradeSchedule\": {\n" + " \"upgradeMode\": \"automatic\"\n" + " }\n" + " }\n" + "}", + "{" + " \"manifest\" : \"A\"," + " \"policy\" : \"B\"," + " \"settings\" : \"C\"," + " \"data\" : \"D\"" + "}" + ); + + CheckUpdateRequest req("", "", "", "", I_OrchestrationTools::SELECTED_CHECKSUM_TYPE_STR, "12"); + setUpgradeFields(req); + auto response = checkUpdate(req); + EXPECT_TRUE(response.ok()); + + EXPECT_THAT(req.getManifest(), IsValue("A")); + EXPECT_THAT(req.getPolicy(), IsValue("B")); + EXPECT_THAT(req.getSettings(), IsValue("C")); +} + +TEST_F(FogCommunicationTest, sendPolicyVersion) +{ + EXPECT_CALL(mock_ot, doesFileExist(required_apps_file_path)).WillOnce(Return(false)); + init(); + string msg = ""; + + EXPECT_CALL( + mock_message, + sendSyncMessage( + HTTPMethod::PATCH, + "/agents", + "{ \"policyVersion\" :\"12\", \"versions\": [\n" + "{\n" + " \"name\": \"Max\",\n" + " \"id\": \"12345\",\n" + " \"version\": 5\n},\n" + "{\n" + " \"name\": \"Tom\",\n" + " \"id\": \"67890\",\n" + " \"version\": 6\n" + "}]}", + _, + _ + ) + ).WillOnce(Return(HTTPResponse(HTTPStatusCode::HTTP_OK, msg))); + string policy_versions = + "[\n" + "{\n" + " \"name\": \"Max\",\n" + " \"id\": \"12345\",\n" + " \"version\": 5\n},\n" + "{\n" + " \"name\": \"Tom\",\n" + " \"id\": \"67890\",\n" + " \"version\": 6\n" + "}]"; + sendPolicyVersion("12", policy_versions); +} + +TEST_F(FogCommunicationTest, virtual_check_update) +{ + EXPECT_CALL(mock_ot, doesFileExist(required_apps_file_path)).WillOnce(Return(false)); + init(); + // Reading user cred + Maybe no_cred_err(genError("No Credentials file")); + EXPECT_CALL(mock_ot, readFile(data_path + user_cred_file_name)).WillOnce(Return(no_cred_err)); + + // Reading OTP + EXPECT_CALL(mock_ot, readFile("/etc/cp/conf/registration-data.json")).WillOnce(Return(base64_otp)); + EXPECT_CALL(mock_ot, base64Decode(base64_otp)).WillOnce(Return(clear_otp)); + EXPECT_CALL(mock_ot, removeFile("/etc/cp/conf/registration-data.json")).WillOnce(Return(true)); + + // Reading agent details for registration + Maybe> no_nginx(genError("No nginx")); + EXPECT_CALL(mock_details_resolver, getHostname()).WillOnce(Return(string("smartmeter"))); + EXPECT_CALL(mock_details_resolver, getPlatform()).WillOnce(Return(string("gaia"))); + EXPECT_CALL(mock_details_resolver, getArch()).WillOnce(Return(string("x86_64"))); + EXPECT_CALL(mock_details_resolver, isReverseProxy()).WillOnce(Return(true)); + EXPECT_CALL(mock_details_resolver, isKernelVersion3OrHigher()).WillOnce(Return(false)); + EXPECT_CALL(mock_details_resolver, isGwNotVsx()).WillOnce(Return(false)); + EXPECT_CALL(mock_details_resolver, isVersionEqualOrAboveR8110()).WillOnce(Return(false)); + EXPECT_CALL(mock_details_resolver, getResolvedDetails()).WillOnce(Return(map())); + EXPECT_CALL(mock_details_resolver, parseNginxMetadata()).WillOnce(Return(no_nginx)); + EXPECT_CALL(mock_details_resolver, getAgentVersion()) + .WillOnce(Return(Version::getFullVersion())) + .WillOnce(Return(Version::getFullVersion())); + + EXPECT_CALL(mock_status, setAgentDetails(agent_id, profile_id, tenant_id)); + EXPECT_CALL(mock_status, + setRegistrationDetails("smartmeter", "Embedded", "gaia", "x86_64")); + EXPECT_CALL( + mock_status, + setFieldStatus(OrchestrationStatusFieldType::REGISTRATION, OrchestrationStatusResult::SUCCESS, "") + ).Times(2); + + // Sending cred request + expectAuthenticationData( + "{\n" + " \"authenticationData\": [\n" + " {\n" + " \"authenticationMethod\": \"token\",\n" + " \"data\": \"This is the best OTP token\"\n" + " }\n" + " ],\n" + " \"metaData\": {\n" + " \"agentName\": \"smartmeter\",\n" + " \"agentType\": \"Embedded\",\n" + " \"platform\": \"gaia\",\n" + " \"architecture\": \"x86_64\",\n" + " \"agentVersion\": \"" + Version::getFullVersion() + "\",\n" + " \"additionalMetaData\": {\n" + " \"agent_version\": \"" + Version::getFullVersion() + "\",\n" + " \"managedMode\": \"management\",\n" + " \"reverse_proxy\": \"true\",\n" + " \"userEdition\": \"PrEm1um%\"\n" + " }\n" + " }\n" + "}" + ); + + // Saving cred + EXPECT_CALL(mock_encryptor, aes256EncryptWithSizePad(clear_cred)).WillOnce(Return(mb_base64_otp)); + EXPECT_CALL(mock_ot, writeFile(base64_otp, data_path + user_cred_file_name, false)).WillOnce(Return(true)); + + // Creating the session token routine + EXPECT_CALL(mock_ml, doesRoutineExist(0)).WillOnce(Return(false)); + I_MainLoop::Routine routine; + EXPECT_CALL(mock_ml, addOneTimeRoutine(I_MainLoop::RoutineType::RealTime, _, _, true)) + .WillOnce(DoAll(SaveArg<1>(&routine), Return(1))); + EXPECT_CALL(mock_ml, yield(chrono::microseconds(11000000))); + EXPECT_FALSE(authenticateAgent().ok()); + + // Looping the routine + EXPECT_CALL(mock_ot, base64Encode("user id:best shared secret")) + .WillOnce(Return(string("dXNlciBpZDpiZXN0IHNoYXJlZCBzZWNyZXQ="))); + + expectTokenRequest(); + EXPECT_CALL( + mock_encryptor, + aes256EncryptWithSizePad(clear_access_token) + ).WillOnce(Return(mb_encrypted_access_token)); + EXPECT_CALL( + mock_ot, + writeFile(encrypted_access_token, data_path + session_token_file_name, false)).WillOnce(Return(true) + ); + EXPECT_CALL(mock_ml, yield(A())).WillOnce( + Invoke( + [] (chrono::microseconds microseconds) + { + EXPECT_EQ(10000000, microseconds.count()); // Validate short expiration time, mininum is 10 sec + throw invalid_argument("stop while loop"); + } + ) + ); + try { + routine(); + } catch (const invalid_argument& e) {} + EXPECT_CALL(mock_ml, doesRoutineExist(1)).WillOnce(Return(true)); + EXPECT_TRUE(authenticateAgent().ok()); + + // Sending checkupdate request + expectCheckupdateRequest( + "{\n" + " \"manifest\": \"\",\n" + " \"policy\": \"\",\n" + " \"settings\": \"\",\n" + " \"data\": \"\",\n" + " \"virtualSettings\": {\n" + " \"tenants\": []\n" + " },\n" + " \"virtualPolicy\": {\n" + " \"tenants\": [\n" + " {\n" + " \"tenantId\": \"\",\n" + " \"profileId\": \"\",\n" + " \"checksum\": \"\",\n" + " \"version\": \"\"\n" + " },\n" + " {\n" + " \"tenantId\": \"1\",\n" + " \"profileId\": \"4\",\n" + " \"checksum\": \"2\",\n" + " \"version\": \"3\"\n" + " },\n" + " {\n" + " \"tenantId\": \"tenant_id\",\n" + " \"profileId\": \"profile_id\",\n" + " \"checksum\": \"checksum\",\n" + " \"version\": \"version\"\n" + " }\n" + " ]\n" + " },\n" + " \"checksum-type\": \"sha256sum\",\n" + " \"policyVersion\": \"102\",\n" + " \"localConfigurationSettings\": {\n" + " \"upgradeSchedule\": {\n" + " \"upgradeMode\": \"manual\"\n" + " }\n" + " }\n" + "}", + "{" + " \"manifest\" : \"A\"," + " \"policy\" : \"B\"," + " \"settings\" : \"C\"," + " \"data\" : \"D\"," + " \"virtualPolicy\": {\n" + " \"tenants\": [\n" + " {\n" + " \"tenantId\": \"\",\n" + " \"profileId\": \"\",\n" + " \"checksum\": \"\",\n" + " \"version\": \"\"\n" + " },\n" + " {\n" + " \"tenantId\": \"1\",\n" + " \"profileId\": \"4\",\n" + " \"checksum\": \"2\",\n" + " \"version\": \"3\"\n" + " },\n" + " {\n" + " \"tenantId\": \"tenant_id\",\n" + " \"profileId\": \"profile_id\",\n" + " \"checksum\": \"checksum\",\n" + " \"version\": \"version\"\n" + " }\n" + " ]\n" + " }\n" + "}" + ); + CheckUpdateRequest req("", "", "", "", I_OrchestrationTools::SELECTED_CHECKSUM_TYPE_STR, "102"); + req.addTenantPolicy("", "", "", ""); + req.addTenantPolicy("1", "4", "2", "3"); + req.addTenantPolicy("tenant_id", "profile_id", "checksum", "version"); + setUpgradeFields(req); + + auto response = checkUpdate(req); + EXPECT_TRUE(response.ok()); + + EXPECT_THAT(req.getManifest(), IsValue("A")); + EXPECT_THAT(req.getPolicy(), IsValue("B")); + EXPECT_THAT(req.getSettings(), IsValue("C")); + EXPECT_THAT(req.getData(), IsValue("D")); + + auto res = req.getVirtualPolicy(); + EXPECT_TRUE(res.ok()); + + vector exp; + + exp.push_back(CheckUpdateRequest::Tenants("", "", "", "")); + exp.push_back(CheckUpdateRequest::Tenants("1", "4", "2", "3")); + exp.push_back(CheckUpdateRequest::Tenants("tenant_id", "profile_id", "checksum", "version")); + + EXPECT_THAT(res, exp); +} + +TEST_F(FogCommunicationTest, empty_virtual_check_update) +{ + EXPECT_CALL(mock_ot, doesFileExist(required_apps_file_path)).WillOnce(Return(false)); + init(); + // Reading user cred + Maybe no_cred_err(genError("No Credentials file")); + EXPECT_CALL(mock_ot, readFile(data_path + user_cred_file_name)).WillOnce(Return(no_cred_err)); + + // Reading OTP + EXPECT_CALL(mock_ot, readFile("/etc/cp/conf/registration-data.json")).WillOnce(Return(base64_otp)); + EXPECT_CALL(mock_ot, base64Decode(base64_otp)).WillOnce(Return(clear_otp)); + EXPECT_CALL(mock_ot, removeFile("/etc/cp/conf/registration-data.json")).WillOnce(Return(true)); + + // Reading agent details for registration + Maybe> no_nginx(genError("No nginx")); + EXPECT_CALL(mock_details_resolver, getHostname()).WillOnce(Return(string("smartmeter"))); + EXPECT_CALL(mock_details_resolver, getPlatform()).WillOnce(Return(string("gaia"))); + EXPECT_CALL(mock_details_resolver, getArch()).WillOnce(Return(string("x86_64"))); + EXPECT_CALL(mock_details_resolver, isReverseProxy()).WillOnce(Return(true)); + EXPECT_CALL(mock_details_resolver, isKernelVersion3OrHigher()).WillOnce(Return(false)); + EXPECT_CALL(mock_details_resolver, isGwNotVsx()).WillOnce(Return(false)); + EXPECT_CALL(mock_details_resolver, isVersionEqualOrAboveR8110()).WillOnce(Return(false)); + EXPECT_CALL(mock_details_resolver, getResolvedDetails()).WillOnce(Return(map())); + EXPECT_CALL(mock_details_resolver, parseNginxMetadata()).WillOnce(Return(no_nginx)); + EXPECT_CALL(mock_details_resolver, getAgentVersion()) + .WillOnce(Return(Version::getFullVersion())) + .WillOnce(Return(Version::getFullVersion())); + + EXPECT_CALL(mock_status, setAgentDetails(agent_id, profile_id, tenant_id)); + EXPECT_CALL(mock_status, + setRegistrationDetails("smartmeter", "Embedded", "gaia", "x86_64")); + EXPECT_CALL( + mock_status, + setFieldStatus(OrchestrationStatusFieldType::REGISTRATION, OrchestrationStatusResult::SUCCESS, "") + ).Times(2); + + // Sending cred request + expectAuthenticationData( + "{\n" + " \"authenticationData\": [\n" + " {\n" + " \"authenticationMethod\": \"token\",\n" + " \"data\": \"This is the best OTP token\"\n" + " }\n" + " ],\n" + " \"metaData\": {\n" + " \"agentName\": \"smartmeter\",\n" + " \"agentType\": \"Embedded\",\n" + " \"platform\": \"gaia\",\n" + " \"architecture\": \"x86_64\",\n" + " \"agentVersion\": \"" + Version::getFullVersion() + "\",\n" + " \"additionalMetaData\": {\n" + " \"agent_version\": \"" + Version::getFullVersion() + "\",\n" + " \"managedMode\": \"management\",\n" + " \"reverse_proxy\": \"true\",\n" + " \"userEdition\": \"PrEm1um%\"\n" + " }\n" + " }\n" + "}" + ); + + // Saving cred + EXPECT_CALL(mock_encryptor, aes256EncryptWithSizePad(clear_cred)).WillOnce(Return(mb_base64_otp)); + EXPECT_CALL(mock_ot, writeFile(base64_otp, data_path + user_cred_file_name, false)).WillOnce(Return(true)); + + // Creating the session token routine + EXPECT_CALL(mock_ml, doesRoutineExist(0)).WillOnce(Return(false)); + I_MainLoop::Routine routine; + EXPECT_CALL(mock_ml, addOneTimeRoutine(I_MainLoop::RoutineType::RealTime, _, _, true)) + .WillOnce(DoAll(SaveArg<1>(&routine), Return(1))); + EXPECT_CALL(mock_ml, yield(chrono::microseconds(11000000))); + EXPECT_FALSE(authenticateAgent().ok()); + + // Looping the routine + EXPECT_CALL(mock_ot, base64Encode("user id:best shared secret")) + .WillOnce(Return(string("dXNlciBpZDpiZXN0IHNoYXJlZCBzZWNyZXQ="))); + + expectTokenRequest(); + EXPECT_CALL(mock_encryptor, aes256EncryptWithSizePad(clear_access_token)) + .WillOnce(Return(mb_encrypted_access_token)); + EXPECT_CALL( + mock_ot, + writeFile(encrypted_access_token, data_path + session_token_file_name, false)).WillOnce(Return(true) + ); + EXPECT_CALL(mock_ml, yield(A())).WillOnce( + Invoke( + [] (chrono::microseconds microseconds) + { + EXPECT_EQ(10000000, microseconds.count()); // Validate short expiration time, mininum is 10 sec + throw invalid_argument("stop while loop"); + } + ) + ); + try { + routine(); + } catch (const invalid_argument& e) {} + EXPECT_CALL(mock_ml, doesRoutineExist(1)).WillOnce(Return(true)); + EXPECT_TRUE(authenticateAgent().ok()); + + // Sending checkupdate request + expectCheckupdateRequest( + "{\n" + " \"manifest\": \"\",\n" + " \"policy\": \"\",\n" + " \"settings\": \"\",\n" + " \"data\": \"\",\n" + " \"virtualSettings\": {\n" + " \"tenants\": []\n" + " },\n" + " \"virtualPolicy\": {\n" + " \"tenants\": [\n" + " {\n" + " \"tenantId\": \"\",\n" + " \"profileId\": \"\",\n" + " \"checksum\": \"\",\n" + " \"version\": \"\"\n" + " },\n" + " {\n" + " \"tenantId\": \"1\",\n" + " \"profileId\": \"4\",\n" + " \"checksum\": \"2\",\n" + " \"version\": \"3\"\n" + " },\n" + " {\n" + " \"tenantId\": \"tenant_id\",\n" + " \"profileId\": \"profile_id\",\n" + " \"checksum\": \"checksum\",\n" + " \"version\": \"version\"\n" + " }\n" + " ]\n" + " },\n" + " \"checksum-type\": \"sha256sum\",\n" + " \"policyVersion\": \"102\"\n" + "}", + "{" + " \"manifest\" : \"A\"," + " \"policy\" : \"B\"," + " \"settings\" : \"C\"," + " \"data\" : \"D\"" + "}" + + ); + + CheckUpdateRequest req("", "", "", "", I_OrchestrationTools::SELECTED_CHECKSUM_TYPE_STR, "102"); + req.addTenantPolicy("", "", "", ""); + req.addTenantPolicy("1", "4", "2", "3"); + req.addTenantPolicy("tenant_id", "profile_id", "checksum", "version"); + + + auto response = checkUpdate(req); + EXPECT_TRUE(response.ok()); + + EXPECT_THAT(req.getManifest(), IsValue("A")); + EXPECT_THAT(req.getPolicy(), IsValue("B")); + EXPECT_THAT(req.getSettings(), IsValue("C")); + EXPECT_THAT(req.getData(), IsValue("D")); + + auto res = req.getVirtualPolicy(); + EXPECT_FALSE(res.ok()); +} + +TEST_F(FogCommunicationTest, greedy_check_update) +{ + EXPECT_CALL(mock_ot, doesFileExist(required_apps_file_path)).WillOnce(Return(false)); + init(); + // Reading user cred + Maybe no_cred_err(genError("No Credentials file")); + EXPECT_CALL(mock_ot, readFile(data_path + user_cred_file_name)).WillOnce(Return(no_cred_err)); + + // Reading OTP + EXPECT_CALL(mock_ot, readFile("/etc/cp/conf/registration-data.json")).WillOnce(Return(base64_otp)); + EXPECT_CALL(mock_ot, base64Decode(base64_otp)).WillOnce(Return(clear_otp)); + EXPECT_CALL(mock_ot, removeFile("/etc/cp/conf/registration-data.json")).WillOnce(Return(true)); + + // Reading agent details for registration + Maybe> no_nginx(genError("No nginx")); + EXPECT_CALL(mock_details_resolver, getHostname()).WillOnce(Return(string("smartmeter"))); + EXPECT_CALL(mock_details_resolver, getPlatform()).WillOnce(Return(string("gaia"))); + EXPECT_CALL(mock_details_resolver, getArch()).WillOnce(Return(string("x86_64"))); + EXPECT_CALL(mock_details_resolver, isReverseProxy()).WillOnce(Return(true)); + EXPECT_CALL(mock_details_resolver, isKernelVersion3OrHigher()).WillOnce(Return(false)); + EXPECT_CALL(mock_details_resolver, isGwNotVsx()).WillOnce(Return(false)); + EXPECT_CALL(mock_details_resolver, isVersionEqualOrAboveR8110()).WillOnce(Return(false)); + EXPECT_CALL(mock_details_resolver, getResolvedDetails()).WillOnce(Return(map())); + EXPECT_CALL(mock_details_resolver, parseNginxMetadata()).WillOnce(Return(no_nginx)); + EXPECT_CALL(mock_details_resolver, getAgentVersion()) + .WillOnce(Return(Version::getFullVersion())) + .WillOnce(Return(Version::getFullVersion())); + + EXPECT_CALL(mock_status, setAgentDetails(agent_id, profile_id, tenant_id)); + EXPECT_CALL(mock_status, + setRegistrationDetails("smartmeter", "Embedded", "gaia", "x86_64")); + EXPECT_CALL( + mock_status, + setFieldStatus(OrchestrationStatusFieldType::REGISTRATION, OrchestrationStatusResult::SUCCESS, "") + ).Times(2); + + // Sending cred request + expectAuthenticationData( + "{\n" + " \"authenticationData\": [\n" + " {\n" + " \"authenticationMethod\": \"token\",\n" + " \"data\": \"This is the best OTP token\"\n" + " }\n" + " ],\n" + " \"metaData\": {\n" + " \"agentName\": \"smartmeter\",\n" + " \"agentType\": \"Embedded\",\n" + " \"platform\": \"gaia\",\n" + " \"architecture\": \"x86_64\",\n" + " \"agentVersion\": \"" + Version::getFullVersion() + "\",\n" + " \"additionalMetaData\": {\n" + " \"agent_version\": \"" + Version::getFullVersion() + "\",\n" + " \"managedMode\": \"management\",\n" + " \"reverse_proxy\": \"true\",\n" + " \"userEdition\": \"PrEm1um%\"\n" + " }\n" + " }\n" + "}" + ); + + // Saving cred + EXPECT_CALL(mock_encryptor, aes256EncryptWithSizePad(clear_cred)).WillOnce(Return(mb_base64_otp)); + EXPECT_CALL(mock_ot, writeFile(base64_otp, data_path + user_cred_file_name, false)).WillOnce(Return(true)); + + // Creating the session token routine + EXPECT_CALL(mock_ml, doesRoutineExist(0)).WillOnce(Return(false)); + I_MainLoop::Routine routine; + EXPECT_CALL(mock_ml, addOneTimeRoutine(I_MainLoop::RoutineType::RealTime, _, _, true)) + .WillOnce(DoAll(SaveArg<1>(&routine), Return(1))); + EXPECT_CALL(mock_ml, yield(chrono::microseconds(11000000))); + EXPECT_FALSE(authenticateAgent().ok()); + + // Looping the routine + EXPECT_CALL(mock_ot, base64Encode("user id:best shared secret")) + .WillOnce(Return(string("dXNlciBpZDpiZXN0IHNoYXJlZCBzZWNyZXQ="))); + + expectTokenRequest(); + EXPECT_CALL( + mock_encryptor, + aes256EncryptWithSizePad(clear_access_token) + ).WillOnce(Return(mb_encrypted_access_token)); + EXPECT_CALL( + mock_ot, + writeFile(encrypted_access_token, data_path + session_token_file_name, false)).WillOnce(Return(true) + ); + EXPECT_CALL(mock_ml, yield(A())).WillOnce( + Invoke( + [] (chrono::microseconds microseconds) + { + EXPECT_EQ(10000000, microseconds.count()); // Validate short expiration time, mininum is 10 sec + throw invalid_argument("stop while loop"); + } + ) + ); + try { + routine(); + } catch (const invalid_argument& e) {} + EXPECT_CALL(mock_ml, doesRoutineExist(1)).WillOnce(Return(true)); + EXPECT_TRUE(authenticateAgent().ok()); + + // Sending checkupdate request + expectCheckupdateRequest( + "{\n" + " \"manifest\": \"\",\n" + " \"policy\": \"\",\n" + " \"settings\": \"\",\n" + " \"data\": \"\",\n" + " \"virtualSettings\": {\n" + " \"tenants\": []\n" + " },\n" + " \"virtualPolicy\": {\n" + " \"tenants\": []\n" + " },\n" + " \"checkForAllTenants\": true,\n" + " \"checksum-type\": \"sha256sum\",\n" + " \"policyVersion\": \"102\"\n" + "}", + "{" + " \"manifest\" : \"A\"," + " \"policy\" : \"B\"," + " \"settings\" : \"C\"," + " \"data\" : \"D\"," + " \"virtualPolicy\": {\n" + " \"tenants\": [\n" + " {\n" + " \"tenantId\": \"\",\n" + " \"profileId\": \"\",\n" + " \"checksum\": \"\",\n" + " \"version\": \"\"\n" + " },\n" + " {\n" + " \"tenantId\": \"1\",\n" + " \"profileId\": \"4\",\n" + " \"checksum\": \"2\",\n" + " \"version\": \"3\"\n" + " },\n" + " {\n" + " \"tenantId\": \"tenant_id\",\n" + " \"profileId\": \"profile_id\",\n" + " \"checksum\": \"checksum\",\n" + " \"version\": \"version\"\n" + " }\n" + " ]\n" + " }\n" + "}" + ); + + CheckUpdateRequest req("", "", "", "", I_OrchestrationTools::SELECTED_CHECKSUM_TYPE_STR, "102"); + req.setGreedyMode(); + + auto response = checkUpdate(req); + EXPECT_TRUE(response.ok()); + + EXPECT_THAT(req.getManifest(), IsValue("A")); + EXPECT_THAT(req.getPolicy(), IsValue("B")); + EXPECT_THAT(req.getSettings(), IsValue("C")); + EXPECT_THAT(req.getData(), IsValue("D")); + + auto res = req.getVirtualPolicy(); + EXPECT_TRUE(res.ok()); + + vector exp; + + exp.push_back(CheckUpdateRequest::Tenants("", "", "", "")); + exp.push_back(CheckUpdateRequest::Tenants("1", "4", "2", "3")); + exp.push_back(CheckUpdateRequest::Tenants("tenant_id", "profile_id", "checksum", "version")); + + EXPECT_THAT(res, exp); +} + +#endif //DISABLE_APPSEC_DATA_ENCRYPTION diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 73caf65..2b7d2de 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -1,4 +1,4 @@ -#add_subdirectory(cptest) +add_subdirectory(cptest) add_subdirectory(agent_core_utilities) add_subdirectory(shell_cmd) add_subdirectory(debug_is) diff --git a/core/include/services_sdk/resources/report/report_enums.h b/core/include/services_sdk/resources/report/report_enums.h index 999fad6..5b7fe1c 100755 --- a/core/include/services_sdk/resources/report/report_enums.h +++ b/core/include/services_sdk/resources/report/report_enums.h @@ -66,6 +66,8 @@ enum class Tags { CROWDSEC, PLAYGROUND, API_DISCOVERY, + NGINX_PROXY_MANAGER, + WEB_SERVER_APISIX, COUNT }; diff --git a/core/report/tag_and_enum_management.cc b/core/report/tag_and_enum_management.cc index 971af9f..85a0028 100755 --- a/core/report/tag_and_enum_management.cc +++ b/core/report/tag_and_enum_management.cc @@ -108,7 +108,9 @@ TagAndEnumManagement::convertStringToTag(const string &tag) {"Horizon Telemetry Metrics", ReportIS::Tags::HORIZON_TELEMETRY_METRICS}, {"Crowdsec", ReportIS::Tags::CROWDSEC}, {"apiDiscoveryCloudMessaging", ReportIS::Tags::API_DISCOVERY}, - {"Playground", ReportIS::Tags::PLAYGROUND} + {"Playground", ReportIS::Tags::PLAYGROUND}, + {"Nginx Proxy Manager", ReportIS::Tags::NGINX_PROXY_MANAGER}, + {"APISIX Server", ReportIS::Tags::WEB_SERVER_APISIX} }; auto report_is_tag = strings_to_tags.find(tag); @@ -314,7 +316,9 @@ EnumArray TagAndEnumManagement::tags_translation_arr { "Horizon Telemetry Metrics", "Crowdsec", "Playground", - "apiDiscoveryCloudMessaging" + "apiDiscoveryCloudMessaging", + "Nginx Proxy Manager", + "APISIX Server" }; EnumArray TagAndEnumManagement::audience_team_translation { diff --git a/nodes/http_transaction_handler/package/CMakeLists.txt b/nodes/http_transaction_handler/package/CMakeLists.txt index 829c01c..8e0166c 100755 --- a/nodes/http_transaction_handler/package/CMakeLists.txt +++ b/nodes/http_transaction_handler/package/CMakeLists.txt @@ -5,6 +5,10 @@ install(FILES cp-nano-http-transaction-handler-debug-conf.json DESTINATION http_ install(FILES cp-nano-http-transaction-handler.cfg DESTINATION http_transaction_handler_service/conf PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ) install(FILES k8s-log-file-handler.sh DESTINATION http_transaction_handler_service/bin PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ) +install(DIRECTORY snort3_to_ips/ DESTINATION http_transaction_handler_service/scripts/snort3_to_ips FILES_MATCHING PATTERN "*" PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) +install(FILES snort_to_ips_local.py DESTINATION http_transaction_handler_service/scripts PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ) +install(FILES exception.py DESTINATION http_transaction_handler_service/scripts PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ) + #install(DIRECTORY ${ng_module_osrc_pcre2_path}/lib/ DESTINATION http_transaction_handler_service/lib/ FILES_MATCHING PATTERN "libpcre2-8.so*" PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ) #install(DIRECTORY ${ng_module_osrc_pcre2_path}/lib/ DESTINATION http_transaction_handler_service/lib/ FILES_MATCHING PATTERN "libpcre2-posix.so*" PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ) #install(RUNTIME_DEPENDENCY_SET xml DESTINATION http_transaction_handler_service/lib/ PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ) diff --git a/nodes/http_transaction_handler/package/exception.py b/nodes/http_transaction_handler/package/exception.py new file mode 100755 index 0000000..d84e06b --- /dev/null +++ b/nodes/http_transaction_handler/package/exception.py @@ -0,0 +1,8 @@ +class SnortHookException(Exception): + + def __init__(self, message="", practice_id=""): + self.message = message + self.practice_id = practice_id + + def __str__(self): + return "{}".format(self.message) diff --git a/nodes/http_transaction_handler/package/install-http-transaction-handler.sh b/nodes/http_transaction_handler/package/install-http-transaction-handler.sh index f05eaa0..40df159 100755 --- a/nodes/http_transaction_handler/package/install-http-transaction-handler.sh +++ b/nodes/http_transaction_handler/package/install-http-transaction-handler.sh @@ -7,6 +7,7 @@ INSTALLATION_TIME=$(date) WAAP_POLICY_FOLDER_PATH=/etc/cp/conf/waap IPS_POLICY_FOLDER_PATH=/etc/cp/conf/ips +SNORT_SCRIPTS_PATH=/etc/cp/scripts/ DEFAULT_HTTP_TRANSACTION_HANDLER_EVENT_BUFFER=/var/log/nano_agent/event_buffer/HTTP_TRANSACTION_HANDLER_events @@ -216,6 +217,10 @@ run_installation() install_policy $is_debug_mode "$var_certs_dir" install_waap + cp_exec "cp -fr scripts/snort3_to_ips $SNORT_SCRIPTS_PATH/snort3_to_ips" + cp_exec "cp -f scripts/exception.py $SNORT_SCRIPTS_PATH/exception.py" + cp_exec "cp -f scripts/snort_to_ips_local.py $SNORT_SCRIPTS_PATH/snort_to_ips_local.py" + ${INSTALL_COMMAND} lib/libshmem_ipc.so /usr/lib/cpnano/ ${INSTALL_COMMAND} lib/libcompression_utils.so /usr/lib/ cp_exec "ldconfig" diff --git a/nodes/http_transaction_handler/package/snort3_to_ips/Signature/DetectionRules.py b/nodes/http_transaction_handler/package/snort3_to_ips/Signature/DetectionRules.py new file mode 100755 index 0000000..2d8cc1e --- /dev/null +++ b/nodes/http_transaction_handler/package/snort3_to_ips/Signature/DetectionRules.py @@ -0,0 +1,53 @@ +from exception import SnortHookException + + +class DetectionRules: + + def __init__(self): + self.data = { + "type": "simple", + "SSM": "", + "keywords": "", + "context": [] + } + + def validate(self): + if self.data["context"] and (len(self.data["keywords"]) or len(self.data["SSM"])): + pass + else: + raise SnortHookException("No detection rule in the rule") + + def set_default_if_needed(self): + pass + + def is_key_valid(self, key): + if key in self.data.keys(): + return True + return False + + def parse_data(self, key, value): + self.parse_func_map[key](self, value) + + def parse_keywords_data(self, value): + if len(self.data["keywords"]) != 0: + self.data["keywords"] += " " + self.data["keywords"] += value + + def parse_context_data(self, value): + if value not in self.data["context"]: + self.data["context"].append(value) + + def parse_ssm_data(self, value): + if self.data["SSM"] != "": + raise SnortHookException("two fast_pattern content") + self.data["SSM"] += value + + def parse_type_data(self, value): + raise SnortHookException("DetectionRules of type not implemented") + + parse_func_map = { + "type": parse_type_data, + "SSM": parse_ssm_data, + "keywords": parse_keywords_data, + "context": parse_context_data + } diff --git a/nodes/http_transaction_handler/package/snort3_to_ips/Signature/IpsKW.py b/nodes/http_transaction_handler/package/snort3_to_ips/Signature/IpsKW.py new file mode 100755 index 0000000..1a06bdd --- /dev/null +++ b/nodes/http_transaction_handler/package/snort3_to_ips/Signature/IpsKW.py @@ -0,0 +1,91 @@ +from exception import SnortHookException + + +class IpsKW: + + def __init__(self, keyword, value, optional_modifier): + self.keyword = keyword + self.value = value + self.optional_modifiers = optional_modifier + + def construct(self): + return self.construct_func_map[self.keyword](self) + + def construct_name(self): + return [("protectionName", self.value)] + + def construct_detection_keyword(self): + constructed_data = [] + optional_modifier_str = "" + for key in self.optional_modifiers: + if key in ['nocase', 'relative']: + optional_modifier_str += ", " + key + elif key == 'depth': + optional_modifier_str += ", {} {}".format(key, self.optional_modifiers[key]) + elif key == 'offset': + if self.optional_modifiers[key] == 0: + continue + optional_modifier_str += ", {} {}".format(key, self.optional_modifiers[key]) + elif key == 'part': + optional_modifier_str += ", {} {}".format(key, self.optional_modifiers[key]) + constructed_data.append(("context", self.optional_modifiers[key])) + else: + raise SnortHookException("Error: Key '{}' is not supported in keywords".format(key)) + constructed_data.append(("keywords", "data: {}{};".format(self.value, optional_modifier_str))) + return constructed_data + + def construct_pcre(self): + constructed_data = [] + optional_modifier_str = "" + for key in self.optional_modifiers: + if key in ['nocase', 'relative']: + optional_modifier_str += ", " + key + elif key in ['offset']: + if self.optional_modifiers[key] == 0: + continue + optional_modifier_str += ", {} {}".format(key, self.optional_modifiers[key]) + elif key == 'part': + optional_modifier_str += ", {} {}".format(key, self.optional_modifiers[key]) + constructed_data.append(("context", self.optional_modifiers[key])) + else: + raise SnortHookException("Error: Key {} is not supported in pcre".format(key)) + constructed_data.append(("keywords", "pcre: {}{};".format(self.value, optional_modifier_str))) + return constructed_data + + def construct_SSM_keyword(self): + constructed_data = [] + for key in self.optional_modifiers: + if key == 'part': + constructed_data.append(("context", self.optional_modifiers[key])) + constructed_data.append(("SSM", self.value.strip("\""))) + + return constructed_data + + def construct_length_keyword(self): + return [("keywords", "{}: {}, {}, part {};".format(self.keyword, self.optional_modifiers['var'], + self.value, self.optional_modifiers['part']))] + + def construct_cvelist(self): + return [(self.keyword, self.value)] + + def construct_severity(self): + return [(self.keyword, self.value)] + + def construct_tags(self): + return [(self.keyword, "Vul_Type_{}".format(self.value))] + + def construct_sid_rev(self): + return [("maintrainId", "{}:{}".format(self.keyword, self.value))] + + construct_func_map = { + 'protectionName': construct_name, + 'keywords': construct_detection_keyword, + 'length': construct_length_keyword, + 'SSM': construct_SSM_keyword, + 'tags': construct_tags, + 'pcre': construct_pcre, + 'cveList': construct_cvelist, + 'severity': construct_severity, + 'sid': construct_sid_rev, + 'rev': construct_sid_rev + } diff --git a/nodes/http_transaction_handler/package/snort3_to_ips/Signature/IpsSignature.py b/nodes/http_transaction_handler/package/snort3_to_ips/Signature/IpsSignature.py new file mode 100755 index 0000000..365c335 --- /dev/null +++ b/nodes/http_transaction_handler/package/snort3_to_ips/Signature/IpsSignature.py @@ -0,0 +1,34 @@ +from snort3_to_ips.Signature.ProtectionMetadata import ProtectionMetadata +from snort3_to_ips.Signature.DetectionRules import DetectionRules +from exception import SnortHookException + + +class IpsSignature: + def __init__(self): + self.protectionMetadata = ProtectionMetadata() + self.detectionRules = DetectionRules() + + def __str__(self): + return str(self.output_signature()) + + def parse_data(self, constructs): + for json_key, json_value in constructs: + if self.protectionMetadata.is_key_valid(json_key): + self.protectionMetadata.parse_data(json_key, json_value) + elif self.detectionRules.is_key_valid(json_key): + self.detectionRules.parse_data(json_key, json_value) + else: + raise SnortHookException("'{}' is not a valid keyword in snort signature".format(json_key)) + + def validate(self): + self.protectionMetadata.validate() + self.detectionRules.validate() + + def set_default_if_needed(self): + self.protectionMetadata.set_default_if_needed() + self.detectionRules.set_default_if_needed() + + def output_signature(self): + self.validate() + self.set_default_if_needed() + return {"protectionMetadata": self.protectionMetadata.data, "detectionRules": self.detectionRules.data} diff --git a/nodes/http_transaction_handler/package/snort3_to_ips/Signature/ProtectionMetadata.py b/nodes/http_transaction_handler/package/snort3_to_ips/Signature/ProtectionMetadata.py new file mode 100755 index 0000000..6dabf9b --- /dev/null +++ b/nodes/http_transaction_handler/package/snort3_to_ips/Signature/ProtectionMetadata.py @@ -0,0 +1,70 @@ +class ProtectionMetadata: + + def __init__(self): + self.data = { + "protectionName": "", + "severity": "", + "confidenceLevel": "", + "performanceImpact": "", + "lastUpdate": "", + "maintrainId": "", + "tags": ["Snort"], + "cveList": [], + "silent": False + } + + def validate(self): + if len(self.data["protectionName"]) == 0: + raise Exception("msg field missing in the snort rule") + + def set_default_if_needed(self): + if self.data["severity"] == "": + self.data["severity"] = "Critical" + if self.data["confidenceLevel"] == "": + self.data["confidenceLevel"] = "High" + if self.data["performanceImpact"] == "": + self.data["performanceImpact"] = "Medium" + if self.data["lastUpdate"] == "": + self.data["lastUpdate"] = "20210909" + + def is_key_valid(self, key): + if key in self.data.keys(): + return True + return False + + def parse_data(self, key, value): + self.parse_func_map[key](self, key, value) + + def parse_name_data(self, key, value): + self.data[key] = value + + def parse_cvelist(self, key, value): + if value not in self.data[key]: + self.data[key].append(value) + + def parse_severity(self, key, value): + self.data[key] = value + + def parse_main_train_id(self, key, value): + if self.data[key] != "": + self.data[key] += " " + self.data[key] += value + + def parse_tags(self, key, value): + if value not in self.data[key]: + self.data[key].append(value) + + def not_implemented(self, key, value): + print("Not implemented for key {} with value {}".format(key, value)) + + parse_func_map = { + "protectionName": parse_name_data, + "severity": parse_severity, + "confidenceLevel": not_implemented, + "performanceImpact": not_implemented, + "lastUpdate": not_implemented, + "maintrainId": parse_main_train_id, + "tags": parse_tags, + "cveList": parse_cvelist, + "silent": not_implemented, + } diff --git a/nodes/http_transaction_handler/package/snort3_to_ips/Signature/Signatures.py b/nodes/http_transaction_handler/package/snort3_to_ips/Signature/Signatures.py new file mode 100755 index 0000000..1b4c768 --- /dev/null +++ b/nodes/http_transaction_handler/package/snort3_to_ips/Signature/Signatures.py @@ -0,0 +1,49 @@ +import json + +from snort3_to_ips.SnortRule.SnortRule import SnortRule +from snort3_to_ips.utils.utils import parse_snort_rules_line, is_invalid_line + +from exception import SnortHookException + + +class Signatures: + def __init__(self): + self.signatures = [] + self.error_rules = [] + self.error_internal = [] + + def load_snort_signatures(self, rules_input): + line_number = 0 + for line in rules_input.split("\n"): + line_number += 1 + if is_invalid_line(line): + continue + rule = line.strip() + header, body = parse_snort_rules_line(rule) + try: + snort_rule = SnortRule(header) + if not snort_rule.is_http_rule(body): + continue + snort_rule.parse_body(body) + self.signatures.append(snort_rule.convert()) + except SnortHookException as se: + self.error_rules.append({"Error": str(se), "Line": line_number}) + except Exception as e: + self.error_internal.append({"Error": str(e), "Line": line_number}) + + def reset(self): + self.signatures = [] + self.error_rules = [] + + def output_errors(self, output_pf): + output = {"Errors": self.error_rules} + with open(output_pf, 'w') as f: + json.dump(output, f, ensure_ascii=False, indent=4) + + def output_ips_signature_package(self, output_pf): + output = {"IPSSnortSigs": {"protections": self.signatures}} + with open(output_pf, 'w') as f: + json.dump(output, f, ensure_ascii=False, indent=4) + + def get_payload_data(self): + return self.signatures, self.error_rules, self.error_internal diff --git a/nodes/http_transaction_handler/package/snort3_to_ips/SnortRule/SnortKW.py b/nodes/http_transaction_handler/package/snort3_to_ips/SnortRule/SnortKW.py new file mode 100755 index 0000000..aecc6f3 --- /dev/null +++ b/nodes/http_transaction_handler/package/snort3_to_ips/SnortRule/SnortKW.py @@ -0,0 +1,190 @@ +from snort3_to_ips.Signature.IpsKW import IpsKW +from snort3_to_ips.utils.utils import is_hex_segment_in_str +from exception import SnortHookException + + +class SnortKW: + + def __init__(self, keyword, value, optional_modifiers): + self.keyword = keyword + self.value = value + self.optional_modifiers = optional_modifiers + + def convert(self, snort_rule): + return self.convert_func_map[self.keyword](self, snort_rule) + + def convert_snort_content_kw(self, snort_rule): + ips_data_modifiers = self.get_ips_modifiers_from_rule(snort_rule) + if ips_data_modifiers.pop('fast_pattern', False) and not is_hex_segment_in_str(self.value): + return [IpsKW("SSM", self.value, ips_data_modifiers)] + return [IpsKW("keywords", self.value, ips_data_modifiers)] + + def convert_snort_pcre_kw(self, snort_rule): + ips_data_modifiers = snort_rule.get_ips_context() + return [IpsKW("pcre", self.value, ips_data_modifiers)] + + def convert_snort_flow_kw(self, snort_rule): + for modifier in self.optional_modifiers: + if modifier == "to_server" or modifier == "from_client": + snort_rule.flow = "client_to_server" + elif modifier == "to_client" or modifier == "from_server": + snort_rule.flow = "server_to_client" + elif modifier in ["established", "not_established", "stateless"]: + pass + elif modifier in ["no_stream", "only_stream"]: + pass + elif modifier in ["no_frag", "only_frag"]: + pass + else: + raise SnortHookException("unsupported modifier for '{}': '{}'".format(self.keyword, modifier)) + return [] + + def convert_snort_msg_kw(self, snort_rule): + return [IpsKW("protectionName", self.value.strip("\""), "")] + + def convert_snort_sticky_buffer(self, snort_rule): + snort_rule.sticky_buffer = self.keyword + if self.value != "": + if self.keyword != "http_header": + raise SnortHookException("arguments are not supported for '{}' of value '{}'".format(self.keyword, self.value)) + if self.value == "field": + snort_rule.dynamic_buffer = self.optional_modifiers + else: + raise SnortHookException("Unknown argument for '{}', '{}'".format(self.keyword, self.value)) + return [] + + def convert_snort_reference(self, snort_rule): + if self.value == 'bugtraq': + return [IpsKW("cveList", "BUGTRAQ-{}".format(self.optional_modifiers[self.value]), "")] + elif self.value == 'cve': + return [IpsKW("cveList", "CVE-{}".format(self.optional_modifiers[self.value]), "")] + elif self.value == 'nessus': + return [IpsKW("cveList", "NESSUS-{}".format(self.optional_modifiers[self.value]), "")] + elif self.value == 'arachnids': + return [IpsKW("cveList", "ARACHNIDS-{}".format(self.optional_modifiers[self.value]), "")] + elif self.value == 'mcafee': + return [IpsKW("cveList", "MCAFEE-{}".format(self.optional_modifiers[self.value]), "")] + elif self.value == 'osvdb': + return [IpsKW("cveList", "OSVDB-{}".format(self.optional_modifiers[self.value]), "")] + elif self.value == 'msb': + return [IpsKW("cveList", "MSB-{}".format(self.optional_modifiers[self.value]), "")] + elif self.value == 'url': + return [IpsKW("cveList", "http://{}".format(self.optional_modifiers[self.value]), "")] + else: + raise SnortHookException("Unknown system in Reference of value: {}".format(self.value)) + + def convert_snort_classtype(self, snort_rule): + return [IpsKW("severity", self.optional_modifiers[self.value], ""), + IpsKW("tags", self.value.replace("-", " ").title().replace(" ", "_"), "")] + + def convert_snort_priority(self, snort_rule): + return [IpsKW("severity", self.value, "")] + + def convert_snort_bufferlen_kw(self, snort_rule): + ips_kw_list = [] + ips_data_modifiers = snort_rule.get_ips_context() + if "<=>" in self.value: + left_var, right_var = self.value.split("<=>") + if not left_var.isnumeric() or not right_var.isnumeric(): + raise SnortHookException("bufferlen - illegal numerical value") + ips_data_modifiers['var'] = left_var + ips_kw_list.append(IpsKW("length", "min", ips_data_modifiers)) + ips_data_modifiers = snort_rule.get_ips_context() + ips_data_modifiers['var'] = right_var + ips_kw_list.append(IpsKW("length", "max", ips_data_modifiers)) + elif "<>" in self.value: + left_var, right_var = self.value.split("<>") + ips_data_modifiers['var'] = left_var + if not left_var.isnumeric() or not right_var.isnumeric(): + raise SnortHookException("bufferlen - illegal numerical value") + ips_kw_list.append(IpsKW("length", "min", ips_data_modifiers)) + ips_data_modifiers = snort_rule.get_ips_context() + ips_data_modifiers['var'] = right_var + ips_kw_list.append(IpsKW("length", "max", ips_data_modifiers)) + elif "<" in self.value: + if not self.value.split("<")[1].isnumeric(): + raise SnortHookException("bufferlen - illegal numerical value") + ips_data_modifiers['var'] = self.value.split("<")[1] + ips_kw_list.append(IpsKW("length", "max", ips_data_modifiers)) + elif ">" in self.value: + ips_data_modifiers['var'] = self.value.split(">")[1] + if not self.value.split(">")[1].isnumeric(): + raise SnortHookException("bufferlen - illegal numerical value") + ips_kw_list.append(IpsKW("length", "min", ips_data_modifiers)) + elif self.value.isnumeric(): + ips_data_modifiers['var'] = self.value + ips_kw_list.append(IpsKW("length", "exact", ips_data_modifiers)) + else: + raise SnortHookException("bufferlen operator is illegal") + return ips_kw_list + + def convert_sid_kw(self, snort_rule): + return [IpsKW("sid", self.value, "")] + + def convert_rev_kw(self, snort_rule): + return [IpsKW("rev", self.value, "")] + + def not_implemented(self, snort_rule): + return [] + + convert_func_map = { + # Primarily functions + 'content': convert_snort_content_kw, + 'pcre': convert_snort_pcre_kw, + 'bufferlen': convert_snort_bufferlen_kw, + 'flow': convert_snort_flow_kw, + # metadata functions + 'msg': convert_snort_msg_kw, + 'reference': convert_snort_reference, + 'gid': not_implemented, + 'sid': convert_sid_kw, + 'rev': convert_rev_kw, + 'classtype': convert_snort_classtype, + 'priority': convert_snort_priority, + 'metadata': not_implemented, + # http functions + 'pkt_data': convert_snort_sticky_buffer, + 'http_uri': convert_snort_sticky_buffer, + 'http_raw_uri': convert_snort_sticky_buffer, + 'http_header': convert_snort_sticky_buffer, + 'http_raw_header': convert_snort_sticky_buffer, + 'http_method': convert_snort_sticky_buffer, + 'http_client_body': convert_snort_sticky_buffer, + 'http_cookie': convert_snort_sticky_buffer, + 'http_raw_cookie': convert_snort_sticky_buffer, + 'http_stat_code': convert_snort_sticky_buffer, + 'http_stat_msg': convert_snort_sticky_buffer, + 'http_encode': convert_snort_sticky_buffer, + # backward compatibility rules. + 'service': not_implemented + } + + def get_ips_modifiers_from_rule(self, snort_rule): + + ips_data_modifiers = {} + + for rule in self.optional_modifiers: + if rule == 'nocase': + ips_data_modifiers['nocase'] = self.optional_modifiers['nocase'] + elif rule == 'depth': + ips_data_modifiers['depth'] = self.optional_modifiers['depth'] + elif rule == 'distance': + if int(self.optional_modifiers['distance']) != 0: + ips_data_modifiers['offset'] = self.optional_modifiers['distance'] + ips_data_modifiers['relative'] = True + elif rule == 'offset': + if int(self.optional_modifiers['offset']) != 0: + ips_data_modifiers['offset'] = self.optional_modifiers['offset'] + elif rule == 'within': + ips_data_modifiers['depth'] = self.optional_modifiers['within'] + ips_data_modifiers['relative'] = True + elif rule == 'fast_pattern': + ips_data_modifiers['fast_pattern'] = True + else: + # print("Error: Not supported convert from {}".format(rule)) + raise SnortHookException("For keyword '{}', unsupported modifier '{}'".format(self.keyword, rule)) + + ips_data_modifiers.update(snort_rule.get_ips_context()) + + return ips_data_modifiers + diff --git a/nodes/http_transaction_handler/package/snort3_to_ips/SnortRule/SnortKWParser.py b/nodes/http_transaction_handler/package/snort3_to_ips/SnortRule/SnortKWParser.py new file mode 100755 index 0000000..2dabff8 --- /dev/null +++ b/nodes/http_transaction_handler/package/snort3_to_ips/SnortRule/SnortKWParser.py @@ -0,0 +1,206 @@ +from exception import SnortHookException + +class SnortKWParser: + + def __init__(self): + pass + + def parse_kw_parameters(self, snort_rule): + tmp_split = snort_rule.split(":") + keyword = tmp_split[0] + return self.parse_func_map[keyword](self, snort_rule) + + def simple_rule(self, snort_rule): + return snort_rule.strip(), "", {} + + def simple_binary_rule(self, snort_rule): + keyword, value = snort_rule.split(":") + return keyword, value, {} + + def parse_content(self, snort_rule): + tmp_split = snort_rule.split(":", 1) + keyword = tmp_split[0] + + value = tmp_split[1][:tmp_split[1].find("\"", 2) + 1].strip() + optional_string = tmp_split[1][tmp_split[1].find("\"", 2) + 1:].strip(",").strip() + + if optional_string == "": + return keyword, value, {} + + optional_modifiers = {} + for optional_command in optional_string.split(","): + optional_command = optional_command.strip() + command = optional_command.strip().split(" ", 1) + if len(command) == 1: + optional_modifiers[command[0]] = True + else: + optional_modifiers[command[0]] = command[1] + return keyword, value, optional_modifiers + + def parse_pcre(self, snort_rule): + tmp_split = snort_rule.split(":", 1) + keyword = tmp_split[0] + + value = tmp_split[1][:tmp_split[1].rfind("\"", 2) + 1].strip() + optional_string = tmp_split[1][tmp_split[1].rfind("/", 1) + 1:tmp_split[1].rfind("\"", 1)].strip() + optional_modifiers = {} + + for modifier in optional_string: + if modifier in 'ismxGARE': + pass + elif modifier in 'O': + raise SnortHookException("unsupported {} modifier {}".format(keyword, modifier)) + else: + raise SnortHookException("Unknown {} modifier {}".format(keyword, modifier)) + + return keyword, value, optional_modifiers + + def parse_reference(self, snort_rule): + keyword = snort_rule.split(":")[0] + value = snort_rule.split(":")[1].split(",")[0] + optional_modifiers = {value: snort_rule.split(":")[1].split(",")[1]} + return keyword, value, optional_modifiers + + def parse_classtype(self, snort_rule): + keyword = snort_rule.split(":")[0] + value = snort_rule.split(":")[1].strip() + optional_modifiers = {value: self.snort_default_classifications[value]} + return keyword, value, optional_modifiers + + def parse_priority(self, snort_rule): + keyword = snort_rule.split(":")[0] + value = int(snort_rule.split(":")[1].strip()) + if value > 4: + value = 4 + return keyword, self.priority_map[value], {} + + def parse_sticky_buffer(self, snort_rule): + split_rule = snort_rule.split(":", 1) + if len(split_rule) == 2: + arguments_split = split_rule[1].split(" ") + return split_rule[0], arguments_split[0].strip(), {arguments_split[0].strip(): arguments_split[1].strip()} + return snort_rule.strip(), "", {} + + def parse_metadata(self, snort_rule): + keyword, value = snort_rule.split(":") + optional_modifiers = {} + for metadata in value.split(","): + key, value = metadata.strip().split(" ", 1) + if key not in optional_modifiers.keys(): + optional_modifiers[key] = set() + optional_modifiers[key].add(value) + return keyword, "", optional_modifiers + + def parse_flow(self, snort_rule): + keyword, value = snort_rule.split(":") + optional_modifiers = [] + for modifier in value.split(','): + optional_modifiers.append(modifier.strip()) + return keyword, value, optional_modifiers + + def parse_service(self, snort_rule): + keyword, value = snort_rule.split(":") + return keyword, value, {} + + def not_implemented(self, snort_rule): + raise SnortHookException("unsupported keyword '{}'".format(snort_rule.split(":")[0])) + + parse_func_map = { + # Primarily functions + 'content': parse_content, + 'pcre': parse_pcre, + 'flow': parse_flow, + 'bufferlen': simple_binary_rule, + # metadata functions + 'msg': simple_binary_rule, + 'reference': parse_reference, + 'gid': simple_binary_rule, + 'sid': simple_binary_rule, + 'rev': simple_binary_rule, + 'classtype': parse_classtype, + 'priority': parse_priority, + 'metadata': parse_metadata, + # http functions + 'pkt_data': simple_rule, + 'http_uri': parse_sticky_buffer, + 'http_raw_uri': parse_sticky_buffer, + 'http_header': parse_sticky_buffer, + 'http_raw_header': parse_sticky_buffer, + 'http_method': parse_sticky_buffer, + 'http_client_body': parse_sticky_buffer, + 'http_cookie': parse_sticky_buffer, + 'http_stat_code': parse_sticky_buffer, + 'http_stat_msg': parse_sticky_buffer, + 'http_raw_cookie': parse_sticky_buffer, + # Snort 2 functions + 'service': parse_service, + # Not implemented + 'byte_test': not_implemented, + 'file_data': not_implemented, + 'byte_jump': not_implemented, + 'isdataat': not_implemented, + 'dsize': not_implemented, + 'icode': not_implemented, + 'flowbits': not_implemented, + 'itype': not_implemented, + 'dce_iface': not_implemented, + 'cmp_id': not_implemented, + 'detection_filter': not_implemented, + 'flags': not_implemented, + 'sip_stat_code': not_implemented, + 'ack': not_implemented, + 'ip_proto': not_implemented, + 'sip_method': not_implemented, + 'asn1': not_implemented, + 'ssl_version': not_implemented, + 'base64_decode': not_implemented, + 'ssl_state': not_implemented, + 'sip_header': not_implemented, + 'fragbits': not_implemented, + } + + priority_map = { + 1: "High", + 2: "Medium", + 3: "Low", + 4: "Very Low" + } + + # Default snort classification for reference. + # If needed custom config, we should provide it. (1 being High, 2 Medium, 3 Low, 4 Very Low) + snort_default_classifications = { + "attempted-admin": "High", + "attempted-user": "High", + "inappropriate-content": "High", + "policy-violation": "High", + "shellcode-detect": "High", + "successful-admin": "High", + "successful-user": "High", + "trojan-activity": "High", + "unsuccessful-user": "High", + "web-application-attack": "High", + "attempted-dos": "Medium", + "attempted-recon": "Medium", + "bad-unknown": "Medium", + "default-login-attempt": "Medium", + "denial-of-service": "Medium", + "misc-attack": "Medium", + "non-standard-protocol": "Medium", + "rpc-portmap-decode": "Medium", + "successful-dos": "Medium", + "successful-recon-largescale": "Medium", + "successful-recon-limited": "Medium", + "suspicious-filename-detect": "Medium", + "suspicious-login": "Medium", + "system-call-detect": "Medium", + "unusual-client-port-connection": "Medium", + "web-application-activity": "Medium", + "icmp-event": "Low", + "misc-activity": "Low", + "network-scan": "Low", + "not-suspicious": "Low", + "protocol-command-decode": "Low", + "string-detect": "Low", + "unknown": "Low", + "tcp-connection": "Very Low" + } diff --git a/nodes/http_transaction_handler/package/snort3_to_ips/SnortRule/SnortRule.py b/nodes/http_transaction_handler/package/snort3_to_ips/SnortRule/SnortRule.py new file mode 100755 index 0000000..5b37176 --- /dev/null +++ b/nodes/http_transaction_handler/package/snort3_to_ips/SnortRule/SnortRule.py @@ -0,0 +1,121 @@ +from snort3_to_ips.SnortRule.SnortRuleHeader import SnortRuleHeader +from snort3_to_ips.SnortRule.SnortKW import SnortKW +from snort3_to_ips.SnortRule.SnortKWParser import SnortKWParser +from snort3_to_ips.Signature.IpsSignature import IpsSignature +from exception import SnortHookException + + +class SnortRule: + + def __init__(self, rule_header): + self.header = SnortRuleHeader(rule_header) + self.keywords = [] + self.sticky_buffer = "Default" + self.dynamic_buffer = {} + self.flow = "client_to_server" + + def is_http_rule(self, parsed_body): + return self.header.is_http_header() or "service:http" in parsed_body + + def add_keyword(self, snort_rule): + kw_parser = SnortKWParser() + keyword, value, optional_modifiers = kw_parser.parse_kw_parameters(snort_rule) + self.keywords.append(SnortKW(keyword, value, optional_modifiers)) + + def parse_body(self, input_snort_rule_body): + for keyword in input_snort_rule_body: + self.add_keyword(keyword) + + def convert(self): + signature = IpsSignature() + for keyword in self.keywords: + for converted_keyword in keyword.convert(self): + signature.parse_data(converted_keyword.construct()) + return signature.output_signature() + + def get_ips_context(self): + return self.convert_http_map[self.sticky_buffer](self) + + def convert_default(self): + return {"part": "HTTP_RAW"} + + def convert_pkt_data(self): + raise SnortHookException("Unsupported keyword 'pkt_data'") + + def convert_http_uri(self): + return {"part": "HTTP_COMPLETE_URL_DECODED"} + + def convert_http_raw_uri(self): + return {"part": "HTTP_COMPLETE_URL_ENCODED"} + + def convert_http_header(self): + if self.flow == "client_to_server": + if 'field' in self.dynamic_buffer.keys(): + return {"part": 'HTTP_REQUEST_HEADER_{}'.format(self.dynamic_buffer['field'].upper())} + else: + return {"part": "HTTP_REQUEST_HEADER"} + elif self.flow == "server_to_client": + if 'field' in self.dynamic_buffer.keys(): + return {"part": 'HTTP_RESPONSE_HEADER_{}'.format(self.dynamic_buffer['field'].upper())} + else: + return {"part": "HTTP_RESPONSE_HEADER"} + + else: + raise SnortHookException("Unknown Flow {}".format(self.flow)) + + def convert_http_raw_header(self): + if self.flow == "client_to_server": + return {"part": "HTTP_REQUEST_HEADER"} + elif self.flow == "server_to_client": + return {"part": "HTTP_RESPONSE_HEADER"} + else: + raise SnortHookException("Unknown Flow {}".format(self.flow)) + + def convert_http_method(self): + return {"part": "HTTP_METHOD"} + + def convert_http_client_body(self): + return {"part": "HTTP_REQUEST_BODY"} + + def convert_http_cookie(self): + if self.flow == "client_to_server": + return {"part": "HTTP_REQUEST_HEADER_COOKIE"} + elif self.flow == "server_to_client": + return {"part": "HTTP_RESPONSE_HEADER_COOKIE"} + else: + raise SnortHookException("Unknown Flow {}".format(self.flow)) + + def convert_http_raw_cookie(self): + if self.flow == "client_to_server": + return {"part": "HTTP_REQUEST_HEADER_COOKIE"} + elif self.flow == "server_to_client": + return {"part": "HTTP_RESPONSE_HEADER_COOKIE"} + else: + raise SnortHookException("Unknown Flow {}".format(self.flow)) + + def convert_http_stat_code(self): + if self.flow == "client_to_server": + raise SnortHookException("http_stat_code isn't supported with flow: to_server, from_client") + elif self.flow == "server_to_client": + return {"part": "HTTP_RESPONSE_CODE"} + else: + raise SnortHookException("Unknown Flow {}".format(self.flow)) + + def not_implemented(self): + raise SnortHookException("unsupported keyword '{}'".format(self.sticky_buffer)) + + convert_http_map = { + 'Default': convert_default, + 'pkt_data': convert_pkt_data, + 'http_uri': convert_http_uri, + 'http_raw_uri': convert_http_raw_uri, + 'http_header': convert_http_header, + 'http_raw_header': convert_http_raw_header, + 'http_method': convert_http_method, + 'http_client_body': convert_http_client_body, + 'http_cookie': convert_http_cookie, + 'http_raw_cookie': convert_http_raw_cookie, + 'http_stat_code': convert_http_stat_code, + 'http_stat_msg': not_implemented, + 'http_encode': not_implemented + } diff --git a/nodes/http_transaction_handler/package/snort3_to_ips/SnortRule/SnortRuleHeader.py b/nodes/http_transaction_handler/package/snort3_to_ips/SnortRule/SnortRuleHeader.py new file mode 100755 index 0000000..75079d6 --- /dev/null +++ b/nodes/http_transaction_handler/package/snort3_to_ips/SnortRule/SnortRuleHeader.py @@ -0,0 +1,60 @@ +from exception import SnortHookException + +class SnortRuleHeader: + def __init__(self, rule_header_str): + self.rules = {} + rule_list = rule_header_str.split(" ") + if len(rule_list) == 2: + # alert http + self.validate_and_parse_action(rule_list[0]) + self.validate_and_parse_protocol(rule_list[1]) + self.rules['source_ports'] = "" + self.rules['destination_ports'] = "" + self.rules['directional_op'] = "" + elif len(rule_list) == 7: + # alert http $HOME_NET any -> $EXTERNAL_NET $HTTP_PORTS + self.validate_and_parse_action(rule_list[0]) + self.validate_and_parse_protocol(rule_list[1]) + self.validate_and_parse_directional_operator(rule_list[4]) + self.validate_and_parse_source_ports(rule_list[3]) + self.validate_and_parse_destination_ports(rule_list[6]) + else: + raise SnortHookException("Invalid Snort rule header") + + def validate_and_parse_action(self, action): + if action in ['drop']: + self.rules['action'] = action + elif action in ['alert', 'log', 'pass', 'reject', 'sdrop']: + pass + # print("Header action {} not supported".format(action)) + else: + raise SnortHookException("Unknown header action {}".format(action)) + + def validate_and_parse_protocol(self, protocol): + if protocol in ['http']: + self.rules['protocol'] = protocol + elif protocol in ['tcp', 'udp', 'icmp', 'ip', 'file']: + self.rules['protocol'] = protocol + else: + raise SnortHookException("Error: Unknown header protocol {}".format(protocol)) + + def validate_and_parse_directional_operator(self, directional_op): + if directional_op in ['->', '<>']: + self.rules['directional_op'] = directional_op + else: + raise SnortHookException("Error: Unknown unsupported header directional operator {}".format(directional_op)) + + def validate_and_parse_source_ports(self, ports): + if ports in ['$HTTP_PORTS']: + self.rules['source_ports'] = ports + else: + self.rules['source_ports'] = "" + + def validate_and_parse_destination_ports(self, ports): + if ports in ['$HTTP_PORTS']: + self.rules['destination_ports'] = ports + else: + self.rules['destination_ports'] = "" + + def is_http_header(self): + return self.rules['protocol'] == 'http' or self.rules['destination_ports'] == "$HTTP_PORTS" diff --git a/nodes/http_transaction_handler/package/snort3_to_ips/main.py b/nodes/http_transaction_handler/package/snort3_to_ips/main.py new file mode 100755 index 0000000..3284f63 --- /dev/null +++ b/nodes/http_transaction_handler/package/snort3_to_ips/main.py @@ -0,0 +1,7 @@ +import snort3_to_ips.Signature.Signatures as Signatures + + +def convert_incoming_rules(rules): + signatures = Signatures.Signatures() + signatures.load_snort_signatures(rules) + return signatures.get_payload_data() diff --git a/nodes/http_transaction_handler/package/snort3_to_ips/utils/utils.py b/nodes/http_transaction_handler/package/snort3_to_ips/utils/utils.py new file mode 100755 index 0000000..7316752 --- /dev/null +++ b/nodes/http_transaction_handler/package/snort3_to_ips/utils/utils.py @@ -0,0 +1,129 @@ +import hashlib +import base64 + +from exception import SnortHookException + + +def parse_snort_rules_line(line): + header = line[0:line.find("(")].strip() + tmp_body = line[line.find("(") + 1:line.rfind(")")].replace("\n", "").strip().split(";") + body = [] + tmp_body_segment = "" + for rule in tmp_body: + if len(rule) == 0: + continue + rule = rule.strip() + if rule[len(rule) - 1] == "\\": + tmp_body_segment += "{};".format(rule[:len(rule) - 1]) + else: + body.append(tmp_body_segment + rule) + tmp_body_segment = "" + + return header, body + + +def is_hex_segment_in_str(value): + if value.count("|") - value.count("\\|") > 0: + return True + return False + + +def is_invalid_line(line): + tmp_line = line.strip() + return len(tmp_line) == 0 or tmp_line[0] == "#" + + +def generate_version_id(data, hash_mode="md5", input_mode="decoded_data"): + if hash_mode == "md5": + hash_mod = hashlib.md5() + elif hash_mode == "sha1": + hash_mod = hashlib.sha1() + else: + return "" + if input_mode == "file": + with open(data, "rb") as f: + for chunk in iter(lambda: f.read(4096), b""): + hash_mod.update(chunk) + elif input_mode == "decoded_data": + hash_mod.update(data.encode()) + return hash_mod.hexdigest() + + +def verify_custom_signatures(signatures): + if signatures is None: + return False + return signatures['isFileExist'] and signatures['size'] != 0 + + +def decode_custom_signature(signatures): + data = signatures.split(",", 1) + metadata = data[0] + if metadata == 'data:': + return "" + elif metadata != 'data:application/octet-stream;base64': + raise SnortHookException("Invalid Snort file") + base64_message = base64.b64decode(data[1]) + return base64_message.decode("utf-8") + + +def prepare_warnings_log(warnings): + if not warnings: + return [] + if len(warnings) <= 10: + ret_warnings = [] + for warning in warnings: + if warning['errorType'] == "SnortRule": + tmp_id = warning['id'] + ret_warnings.append({"id": warning['id'], + "name": "Snort Warning", + "type": "Web Application", + "sub_type": "Snort Conversion", + "message": "Asset {}, skipped line {}: {}".format(warning["assetName"], warning['Line'], warning['Error']) + }) + else: + assets_errors = {} + for warning in warnings: + if warning['errorType'] == "SnortRule": + tmp_id = warning['id'] + asset_name = warning["assetName"] + if asset_name not in assets_errors.keys(): + assets_errors[asset_name] = {} + if warning['Error'] not in assets_errors[asset_name].keys(): + assets_errors[asset_name][warning['Error']] = 1 + else: + assets_errors[asset_name][warning['Error']] += 1 + + ret_warnings = [] + for asset_name in assets_errors.keys(): + for err in assets_errors[asset_name].keys(): + if assets_errors[asset_name][err] == 1: + message_format = "Asset {}: skipped {} {} time" + else: + message_format = "Asset {}: skipped {} {} times" + ret_warnings.append({"id": tmp_id, + "name": "Snort Warning", + "type": "Web Application", + "sub_type": "Snort Conversion", + "message": message_format.format(asset_name, err, assets_errors[asset_name][err]) + }) + for warning in warnings: + if warning['errorType'] != "SnortRule": + tmp_id = warning['id'] + ret_warnings.append({"id": warning['id'], + "name": "Snort Warning", + "type": "Web Application", + "sub_type": "Snort Conversion", + "message": warning['Error'] + }) + if len(ret_warnings) == 1: + final_message_format = "To remove warning, please edit the Snort signatures file" + else: + final_message_format = "To remove warnings, please edit the Snort signatures file" + ret_warnings.append({"id": tmp_id, + "name": "Snort Warning", + "type": "Web Application", + "sub_type": "Snort Conversion", + "message": final_message_format + }) + return ret_warnings + diff --git a/nodes/http_transaction_handler/package/snort_to_ips_local.py b/nodes/http_transaction_handler/package/snort_to_ips_local.py new file mode 100755 index 0000000..6f9e1ab --- /dev/null +++ b/nodes/http_transaction_handler/package/snort_to_ips_local.py @@ -0,0 +1,30 @@ +import os +import snort3_to_ips.Signature.Signatures as Signatures +import sys + + +def convert_snort_to_ips_package(input_pf, output_pf, error_pf): + signatures = Signatures.Signatures() + with open(input_pf) as f: + input_data = f.read() + signatures.load_snort_signatures(input_data) + signatures.output_ips_signature_package(output_pf) + signatures.output_errors(error_pf) + + +if __name__ == '__main__': + if len(sys.argv) < 4: + print("Usage: python3 snort_to_ips_local.py ") + exit(1) + + # Path to snort 3 rules file + in_pf = os.path.join("snort3_to_ips", "data", sys.argv[1]) + + # Path to output file (will create one if it does not exist) + out_pf = os.path.join("snort3_to_ips", "data", sys.argv[2]) + + # Path to output errors file (will create one if it does not exist) + err_pf = os.path.join("snort3_to_ips", "data", sys.argv[3]) + + convert_snort_to_ips_package(in_pf, out_pf, err_pf) +