From 4ddcd2462ab342f146df5a04b050b89cf1ed7ba8 Mon Sep 17 00:00:00 2001 From: Daniel-Eisenberg <59121493+Daniel-Eisenberg@users.noreply.github.com> Date: Wed, 12 Feb 2025 10:56:44 +0200 Subject: [PATCH] Feb 10 2025 dev (#255) * sync code * sync code * code sync * code sync --------- Co-authored-by: Ned Wright Co-authored-by: Daniel Eisenberg --- .../nginx_attachment_util.cc | 12 ++ .../nginx_attachment_util_ut.cc | 4 + build_system/docker/entry.sh | 8 +- .../nginx_attachment/nginx_attachment.cc | 24 ++- .../nginx_attachment_config.cc | 15 ++ .../nginx_attachment_opaque.cc | 47 ++++ .../nginx_attachment_opaque.h | 1 + .../nginx_attachment/nginx_parser.cc | 45 +++- .../nginx_attachment/nginx_parser.h | 5 +- components/http_manager/http_manager.cc | 30 +-- .../http_event_impl/i_http_event_impl.h | 1 + components/include/manifest_handler.h | 1 + components/include/telemetry.h | 49 +++-- components/include/waap.h | 4 +- .../appsec_practice_section.cc | 7 +- .../include/appsec_practice_section.h | 3 +- .../include/policy_activation_data.h | 1 - .../include/policy_maker_utils.h | 3 +- .../local_policy_mgmt_gen/k8s_policy_utils.cc | 8 +- .../new_trusted_sources.cc | 2 +- .../policy_activation_data.cc | 13 -- .../policy_maker_utils.cc | 10 +- .../manifest_controller.cc | 18 +- .../manifest_controller_ut.cc | 152 ++++++++++++- .../manifest_controller/manifest_handler.cc | 25 ++- .../orchestration/orchestration_comp.cc | 137 +++++++++++- .../orchestration_multitenant_ut.cc | 7 + .../orchestration_ut/orchestration_ut.cc | 59 ++++- .../update_communication/fog_authenticator.cc | 6 +- .../security_apps/waap/include/i_serialize.h | 8 +- .../security_apps/waap/include/i_waapConfig.h | 6 +- .../waap/waap_clib/CMakeLists.txt | 1 + .../waap/waap_clib/RequestsMonitor.cc | 158 ++++++++++++++ .../waap/waap_clib/RequestsMonitor.h | 33 +++ .../waap/waap_clib/Serializator.cc | 20 +- .../security_apps/waap/waap_clib/Telemetry.cc | 20 +- .../waap/waap_clib/WaapAssetState.cc | 5 + .../waap/waap_clib/WaapAssetState.h | 4 + .../waap/waap_clib/WaapConfigBase.cc | 31 ++- .../waap/waap_clib/WaapConfigBase.h | 4 +- .../waap/waap_clib/WaapOverrideFunctor.cc | 2 +- .../waap_clib/WaapResponseInjectReasons.cc | 15 ++ .../waap_clib/WaapResponseInjectReasons.h | 3 + .../waap/waap_clib/Waf2Engine.cc | 45 +++- .../security_apps/waap/waap_clib/Waf2Engine.h | 4 +- .../waap/waap_clib/Waf2EngineGetters.cc | 6 +- core/agent_details/agent_details.cc | 6 + .../http_configuration/http_configuration.cc | 4 + core/debug_is/debug.cc | 2 +- .../attachments/nginx_attachment_util.h | 3 + .../services_sdk/interfaces/i_agent_details.h | 2 + .../interfaces/messaging/messaging_metadata.h | 16 +- .../interfaces/mock/mock_agent_details.h | 2 + .../services_sdk/resources/agent_details.h | 3 + .../component_is/components_list_impl.h | 2 + .../services_sdk/resources/debug_flags.h | 1 + .../services_sdk/resources/generic_metric.h | 14 +- .../resources/metric/metric_calc.h | 37 +++- .../resources/metric/metric_map.h | 13 +- .../resources/metric/metric_scraper.h | 45 ++++ .../intelligence_comp_v2.cc | 10 +- core/messaging/connection/connection.cc | 43 +++- core/messaging/connection/connection_comp.cc | 2 +- core/messaging/include/http_request.h | 3 +- core/messaging/messaging_comp/http_request.cc | 4 +- .../messaging_comp/messaging_comp.cc | 8 +- core/metric/CMakeLists.txt | 2 +- core/metric/generic_metric.cc | 97 +++------ core/metric/metric_scraper.cc | 50 +++++ core/metric/metric_ut/metric_ut.cc | 202 +++++++++++++++--- nodes/orchestration/package/CMakeLists.txt | 1 + .../package/open-appsec-cloud-mgmt-k8s | 14 ++ .../package/orchestration_package.sh | 2 + .../watchdog/revert_orchestrator_version.sh | 79 +++++++ nodes/orchestration/package/watchdog/watchdog | 69 ++++++ 75 files changed, 1540 insertions(+), 258 deletions(-) create mode 100644 components/security_apps/waap/waap_clib/RequestsMonitor.cc create mode 100644 components/security_apps/waap/waap_clib/RequestsMonitor.h create mode 100644 core/include/services_sdk/resources/metric/metric_scraper.h create mode 100644 core/metric/metric_scraper.cc create mode 100755 nodes/orchestration/package/watchdog/revert_orchestrator_version.sh diff --git a/attachments/nginx/nginx_attachment_util/nginx_attachment_util.cc b/attachments/nginx/nginx_attachment_util/nginx_attachment_util.cc index ddf3a01..85cd5b6 100644 --- a/attachments/nginx/nginx_attachment_util/nginx_attachment_util.cc +++ b/attachments/nginx/nginx_attachment_util/nginx_attachment_util.cc @@ -95,6 +95,18 @@ getFailOpenHoldTimeout() return conf_data.getNumericalValue("fail_open_hold_timeout"); } +unsigned int +getHoldVerdictPollingTime() +{ + return conf_data.getNumericalValue("hold_verdict_polling_time"); +} + +unsigned int +getHoldVerdictRetries() +{ + return conf_data.getNumericalValue("hold_verdict_retries"); +} + unsigned int getMaxSessionsPerMinute() { diff --git a/attachments/nginx/nginx_attachment_util/nginx_attachment_util_ut/nginx_attachment_util_ut.cc b/attachments/nginx/nginx_attachment_util/nginx_attachment_util_ut/nginx_attachment_util_ut.cc index 2d79ac9..4cefb7c 100644 --- a/attachments/nginx/nginx_attachment_util/nginx_attachment_util_ut/nginx_attachment_util_ut.cc +++ b/attachments/nginx/nginx_attachment_util/nginx_attachment_util_ut/nginx_attachment_util_ut.cc @@ -66,6 +66,8 @@ TEST_F(HttpAttachmentUtilTest, GetValidAttachmentConfiguration) "\"static_resources_path\": \"" + static_resources_path + "\",\n" "\"min_retries_for_verdict\": 1,\n" "\"max_retries_for_verdict\": 3,\n" + "\"hold_verdict_retries\": 3,\n" + "\"hold_verdict_polling_time\": 1,\n" "\"body_size_trigger\": 777,\n" "\"remove_server_header\": 1\n" "}\n"; @@ -97,6 +99,8 @@ TEST_F(HttpAttachmentUtilTest, GetValidAttachmentConfiguration) EXPECT_EQ(getWaitingForVerdictThreadTimeout(), 75u); EXPECT_EQ(getInspectionMode(), ngx_http_inspection_mode::BLOCKING_THREAD); EXPECT_EQ(getRemoveResServerHeader(), 1u); + EXPECT_EQ(getHoldVerdictRetries(), 3u); + EXPECT_EQ(getHoldVerdictPollingTime(), 1u); EXPECT_EQ(isDebugContext("1.2.3.4", "5.6.7.8", 80, "GET", "test", "/abc"), 1); EXPECT_EQ(isDebugContext("1.2.3.9", "5.6.7.8", 80, "GET", "test", "/abc"), 0); diff --git a/build_system/docker/entry.sh b/build_system/docker/entry.sh index 6c8c696..e74c7a6 100644 --- a/build_system/docker/entry.sh +++ b/build_system/docker/entry.sh @@ -98,19 +98,19 @@ while true; do init=true /etc/cp/watchdog/cp-nano-watchdog >/dev/null 2>&1 & sleep 5 - active_watchdog_pid=$(pgrep -f -x -o "/bin/bash /etc/cp/watchdog/cp-nano-watchdog") + active_watchdog_pid=$(pgrep -f -x -o "/bin/(bash|sh) /etc/cp/watchdog/cp-nano-watchdog") fi - current_watchdog_pid=$(pgrep -f -x -o "/bin/bash /etc/cp/watchdog/cp-nano-watchdog") + current_watchdog_pid=$(pgrep -f -x -o "/bin/(bash|sh) /etc/cp/watchdog/cp-nano-watchdog") if [ ! -f /tmp/restart_watchdog ] && [ "$current_watchdog_pid" != "$active_watchdog_pid" ]; then echo "Error: Watchdog exited abnormally" exit 1 elif [ -f /tmp/restart_watchdog ]; then rm -f /tmp/restart_watchdog - kill -9 "$(pgrep -f -x -o "/bin/bash /etc/cp/watchdog/cp-nano-watchdog")" + kill -9 "$(pgrep -f -x -o "/bin/(bash|sh) /etc/cp/watchdog/cp-nano-watchdog")" /etc/cp/watchdog/cp-nano-watchdog >/dev/null 2>&1 & sleep 5 - active_watchdog_pid=$(pgrep -f -x -o "/bin/bash /etc/cp/watchdog/cp-nano-watchdog") + active_watchdog_pid=$(pgrep -f -x -o "/bin/(bash|sh) /etc/cp/watchdog/cp-nano-watchdog") fi sleep 5 diff --git a/components/attachment-intakers/nginx_attachment/nginx_attachment.cc b/components/attachment-intakers/nginx_attachment/nginx_attachment.cc index df6e9a1..3223ffe 100755 --- a/components/attachment-intakers/nginx_attachment/nginx_attachment.cc +++ b/components/attachment-intakers/nginx_attachment/nginx_attachment.cc @@ -31,6 +31,7 @@ #include #include +#include #include #include "nginx_attachment_config.h" @@ -260,6 +261,22 @@ public: ); } + const char* ignored_headers_env = getenv("SAAS_IGNORED_UPSTREAM_HEADERS"); + if (ignored_headers_env) { + string ignored_headers_str = ignored_headers_env; + ignored_headers_str = NGEN::Strings::trim(ignored_headers_str); + + if (!ignored_headers_str.empty()) { + dbgInfo(D_HTTP_MANAGER) + << "Ignoring SAAS_IGNORED_UPSTREAM_HEADERS environment variable: " + << ignored_headers_str; + + vector ignored_headers_vec; + boost::split(ignored_headers_vec, ignored_headers_str, boost::is_any_of(";")); + for (const string &header : ignored_headers_vec) ignored_headers.insert(header); + } + } + dbgInfo(D_NGINX_ATTACHMENT) << "Successfully initialized NGINX Attachment"; } @@ -1034,7 +1051,11 @@ private: case ChunkType::REQUEST_START: return handleStartTransaction(data, opaque); case ChunkType::REQUEST_HEADER: - return handleMultiModifiableChunks(NginxParser::parseRequestHeaders(data), "request header", true); + return handleMultiModifiableChunks( + NginxParser::parseRequestHeaders(data, ignored_headers), + "request header", + true + ); case ChunkType::REQUEST_BODY: return handleModifiableChunk(NginxParser::parseRequestBody(data), "request body", true); case ChunkType::REQUEST_END: { @@ -1814,6 +1835,7 @@ private: HttpAttachmentConfig attachment_config; I_MainLoop::RoutineID attachment_routine_id = 0; bool traffic_indicator = false; + unordered_set ignored_headers; // Interfaces I_Socket *i_socket = nullptr; diff --git a/components/attachment-intakers/nginx_attachment/nginx_attachment_config.cc b/components/attachment-intakers/nginx_attachment/nginx_attachment_config.cc index cf6f1e7..ccbf5c4 100755 --- a/components/attachment-intakers/nginx_attachment/nginx_attachment_config.cc +++ b/components/attachment-intakers/nginx_attachment/nginx_attachment_config.cc @@ -240,6 +240,21 @@ HttpAttachmentConfig::setRetriesForVerdict() "Max retries for verdict" )); + conf_data.setNumericalValue("hold_verdict_retries", getAttachmentConf( + 3, + "agent.retriesForHoldVerdict.nginxModule", + "HTTP manager", + "Retries for hold verdict" + )); + + conf_data.setNumericalValue("hold_verdict_polling_time", getAttachmentConf( + 1, + "agent.holdVerdictPollingInterval.nginxModule", + "HTTP manager", + "Hold verdict polling interval seconds" + )); + + conf_data.setNumericalValue("body_size_trigger", getAttachmentConf( 200000, "agent.reqBodySizeTrigger.nginxModule", diff --git a/components/attachment-intakers/nginx_attachment/nginx_attachment_opaque.cc b/components/attachment-intakers/nginx_attachment/nginx_attachment_opaque.cc index 475a688..a13869f 100755 --- a/components/attachment-intakers/nginx_attachment/nginx_attachment_opaque.cc +++ b/components/attachment-intakers/nginx_attachment/nginx_attachment_opaque.cc @@ -19,12 +19,15 @@ #include "config.h" #include "virtual_modifiers.h" +#include "agent_core_utilities.h" using namespace std; using namespace boost::uuids; USE_DEBUG_FLAG(D_HTTP_MANAGER); +extern bool is_keep_alive_ctx; + NginxAttachmentOpaque::NginxAttachmentOpaque(HttpTransactionData _transaction_data) : TableOpaqueSerialize(this), @@ -119,3 +122,47 @@ NginxAttachmentOpaque::setSavedData(const string &name, const string &data, EnvK saved_data[name] = data; ctx.registerValue(name, data, log_ctx); } + +bool +NginxAttachmentOpaque::setKeepAliveCtx(const string &hdr_key, const string &hdr_val) +{ + if (!is_keep_alive_ctx) return false; + + static pair keep_alive_hdr; + static bool keep_alive_hdr_initialized = false; + + if (keep_alive_hdr_initialized) { + if (!keep_alive_hdr.first.empty() && hdr_key == keep_alive_hdr.first && hdr_val == keep_alive_hdr.second) { + dbgTrace(D_HTTP_MANAGER) << "Registering keep alive context"; + ctx.registerValue("keep_alive_request_ctx", true); + return true; + } + return false; + } + + const char* saas_keep_alive_hdr_name_env = getenv("SAAS_KEEP_ALIVE_HDR_NAME"); + if (saas_keep_alive_hdr_name_env) { + keep_alive_hdr.first = NGEN::Strings::trim(saas_keep_alive_hdr_name_env); + dbgInfo(D_HTTP_MANAGER) << "Using SAAS_KEEP_ALIVE_HDR_NAME environment variable: " << keep_alive_hdr.first; + } + + if (!keep_alive_hdr.first.empty()) { + const char* saas_keep_alive_hdr_value_env = getenv("SAAS_KEEP_ALIVE_HDR_VALUE"); + if (saas_keep_alive_hdr_value_env) { + keep_alive_hdr.second = NGEN::Strings::trim(saas_keep_alive_hdr_value_env); + dbgInfo(D_HTTP_MANAGER) + << "Using SAAS_KEEP_ALIVE_HDR_VALUE environment variable: " + << keep_alive_hdr.second; + } + + if (!keep_alive_hdr.second.empty() && (hdr_key == keep_alive_hdr.first && hdr_val == keep_alive_hdr.second)) { + dbgTrace(D_HTTP_MANAGER) << "Registering keep alive context"; + ctx.registerValue("keep_alive_request_ctx", true); + keep_alive_hdr_initialized = true; + return true; + } + } + + keep_alive_hdr_initialized = true; + return false; +} diff --git a/components/attachment-intakers/nginx_attachment/nginx_attachment_opaque.h b/components/attachment-intakers/nginx_attachment/nginx_attachment_opaque.h index 17c4f5f..2a88926 100755 --- a/components/attachment-intakers/nginx_attachment/nginx_attachment_opaque.h +++ b/components/attachment-intakers/nginx_attachment/nginx_attachment_opaque.h @@ -85,6 +85,7 @@ public: EnvKeyAttr::LogSection log_ctx = EnvKeyAttr::LogSection::NONE ); void setApplicationState(const ApplicationState &app_state) { application_state = app_state; } + bool setKeepAliveCtx(const std::string &hdr_key, const std::string &hdr_val); private: CompressionStream *response_compression_stream; diff --git a/components/attachment-intakers/nginx_attachment/nginx_parser.cc b/components/attachment-intakers/nginx_attachment/nginx_parser.cc index d0f3984..7d5f69c 100755 --- a/components/attachment-intakers/nginx_attachment/nginx_parser.cc +++ b/components/attachment-intakers/nginx_attachment/nginx_parser.cc @@ -29,6 +29,7 @@ USE_DEBUG_FLAG(D_NGINX_ATTACHMENT_PARSER); Buffer NginxParser::tenant_header_key = Buffer(); static const Buffer proxy_ip_header_key("X-Forwarded-For", 15, Buffer::MemoryType::STATIC); static const Buffer source_ip("sourceip", 8, Buffer::MemoryType::STATIC); +bool is_keep_alive_ctx = getenv("SAAS_KEEP_ALIVE_HDR_NAME") != nullptr; map NginxParser::content_encodings = { {Buffer("identity"), CompressionType::NO_COMPRESSION}, @@ -177,22 +178,54 @@ getActivetenantAndProfile(const string &str, const string &deli = ",") } Maybe> -NginxParser::parseRequestHeaders(const Buffer &data) +NginxParser::parseRequestHeaders(const Buffer &data, const unordered_set &ignored_headers) { - auto parsed_headers = genHeaders(data); - if (!parsed_headers.ok()) return parsed_headers.passErr(); + auto maybe_parsed_headers = genHeaders(data); + if (!maybe_parsed_headers.ok()) return maybe_parsed_headers.passErr(); auto i_transaction_table = Singleton::Consume>::by(); + auto parsed_headers = maybe_parsed_headers.unpack(); + NginxAttachmentOpaque &opaque = i_transaction_table->getState(); - for (const HttpHeader &header : *parsed_headers) { + if (is_keep_alive_ctx || !ignored_headers.empty()) { + bool is_last_header_removed = false; + parsed_headers.erase( + remove_if( + parsed_headers.begin(), + parsed_headers.end(), + [&opaque, &is_last_header_removed, &ignored_headers](const HttpHeader &header) + { + string hdr_key = static_cast(header.getKey()); + string hdr_val = static_cast(header.getValue()); + if ( + opaque.setKeepAliveCtx(hdr_key, hdr_val) + || ignored_headers.find(hdr_key) != ignored_headers.end() + ) { + dbgTrace(D_NGINX_ATTACHMENT_PARSER) << "Header was removed from headers list: " << hdr_key; + if (header.isLastHeader()) { + dbgTrace(D_NGINX_ATTACHMENT_PARSER) << "Last header was removed from headers list"; + is_last_header_removed = true; + } + return true; + } + return false; + } + ), + parsed_headers.end() + ); + if (is_last_header_removed) { + dbgTrace(D_NGINX_ATTACHMENT_PARSER) << "Adjusting last header flag"; + if (!parsed_headers.empty()) parsed_headers.back().setIsLastHeader(); + } + } + + for (const HttpHeader &header : parsed_headers) { auto source_identifiers = getConfigurationWithDefault( UsersAllIdentifiersConfig(), "rulebase", "usersIdentifiers" ); source_identifiers.parseRequestHeaders(header); - - NginxAttachmentOpaque &opaque = i_transaction_table->getState(); opaque.addToSavedData( HttpTransactionData::req_headers, static_cast(header.getKey()) + ": " + static_cast(header.getValue()) + "\r\n" diff --git a/components/attachment-intakers/nginx_attachment/nginx_parser.h b/components/attachment-intakers/nginx_attachment/nginx_parser.h index 37c96f0..499fb4a 100755 --- a/components/attachment-intakers/nginx_attachment/nginx_parser.h +++ b/components/attachment-intakers/nginx_attachment/nginx_parser.h @@ -28,7 +28,10 @@ public: static Maybe parseStartTrasaction(const Buffer &data); static Maybe parseResponseCode(const Buffer &data); static Maybe parseContentLength(const Buffer &data); - static Maybe> parseRequestHeaders(const Buffer &data); + static Maybe> parseRequestHeaders( + const Buffer &data, + const std::unordered_set &ignored_headers + ); static Maybe> parseResponseHeaders(const Buffer &data); static Maybe parseRequestBody(const Buffer &data); static Maybe parseResponseBody(const Buffer &raw_response_body, CompressionStream *compression_stream); diff --git a/components/http_manager/http_manager.cc b/components/http_manager/http_manager.cc index 401da1b..30ce762 100755 --- a/components/http_manager/http_manager.cc +++ b/components/http_manager/http_manager.cc @@ -15,18 +15,14 @@ #include #include -#include -#include #include #include -#include #include #include #include #include "common.h" #include "config.h" -#include "table_opaque.h" #include "http_manager_opaque.h" #include "log_generator.h" #include "http_inspection_events.h" @@ -69,22 +65,6 @@ public: i_transaction_table = Singleton::Consume::by(); Singleton::Consume::by()->addGeneralModifier(compressAppSecLogs); - - const char* ignored_headers_env = getenv("SAAS_IGNORED_UPSTREAM_HEADERS"); - if (ignored_headers_env) { - string ignored_headers_str = ignored_headers_env; - ignored_headers_str = NGEN::Strings::removeTrailingWhitespaces(ignored_headers_str); - - if (!ignored_headers_str.empty()) { - dbgInfo(D_HTTP_MANAGER) - << "Ignoring SAAS_IGNORED_UPSTREAM_HEADERS environment variable: " - << ignored_headers_str; - - vector ignored_headers_vec; - boost::split(ignored_headers_vec, ignored_headers_str, boost::is_any_of(";")); - for (const string &header : ignored_headers_vec) ignored_headers.insert(header); - } - } } FilterVerdict @@ -109,19 +89,12 @@ public: return FilterVerdict(default_verdict); } - if (is_request && ignored_headers.find(static_cast(event.getKey())) != ignored_headers.end()) { - dbgTrace(D_HTTP_MANAGER) - << "Ignoring header key - " - << static_cast(event.getKey()) - << " - as it is in the ignored headers list"; - return FilterVerdict(ngx_http_cp_verdict_e::TRAFFIC_VERDICT_INSPECT); - } - ScopedContext ctx; ctx.registerValue(app_sec_marker_key, i_transaction_table->keyToString(), EnvKeyAttr::LogSection::MARKER); HttpManagerOpaque &state = i_transaction_table->getState(); string event_key = static_cast(event.getKey()); + if (event_key == getProfileAgentSettingWithDefault("", "agent.customHeaderValueLogging")) { string event_value = static_cast(event.getValue()); dbgTrace(D_HTTP_MANAGER) @@ -421,7 +394,6 @@ private: I_Table *i_transaction_table; static const ngx_http_cp_verdict_e default_verdict; static const string app_sec_marker_key; - unordered_set ignored_headers; }; const ngx_http_cp_verdict_e HttpManager::Impl::default_verdict(ngx_http_cp_verdict_e::TRAFFIC_VERDICT_DROP); diff --git a/components/include/http_event_impl/i_http_event_impl.h b/components/include/http_event_impl/i_http_event_impl.h index 34633fb..6e8608e 100755 --- a/components/include/http_event_impl/i_http_event_impl.h +++ b/components/include/http_event_impl/i_http_event_impl.h @@ -239,6 +239,7 @@ public: const Buffer & getValue() const { return value; } bool isLastHeader() const { return is_last_header; } + void setIsLastHeader() { is_last_header = true; } uint8_t getHeaderIndex() const { return header_index; } private: diff --git a/components/include/manifest_handler.h b/components/include/manifest_handler.h index 5cfef3e..b21141d 100755 --- a/components/include/manifest_handler.h +++ b/components/include/manifest_handler.h @@ -62,6 +62,7 @@ public: private: Maybe downloadPackage(const Package &package, bool is_clean_installation); + std::string getCurrentTimestamp(); std::string manifest_file_path; std::string temp_ext; diff --git a/components/include/telemetry.h b/components/include/telemetry.h index 0461688..234921a 100755 --- a/components/include/telemetry.h +++ b/components/include/telemetry.h @@ -30,6 +30,7 @@ #include "generic_metric.h" #define LOGGING_INTERVAL_IN_MINUTES 10 +USE_DEBUG_FLAG(D_WAAP); enum class AssetType { API, WEB, ALL, COUNT }; class WaapTelemetryEvent : public Event @@ -132,6 +133,7 @@ private: std::map>& telemetryMap ) { if (!telemetryMap.count(asset_id)) { + dbgTrace(D_WAAP) << "creating telemetry data for asset: " << data.assetName; telemetryMap.emplace(asset_id, std::make_shared()); telemetryMap[asset_id]->init( telemetryName, @@ -139,7 +141,9 @@ private: ReportIS::IssuingEngine::AGENT_CORE, std::chrono::minutes(LOGGING_INTERVAL_IN_MINUTES), true, - ReportIS::Audience::SECURITY + ReportIS::Audience::SECURITY, + false, + asset_id ); telemetryMap[asset_id]->template registerContext( @@ -152,29 +156,30 @@ private: std::string("Web Application"), EnvKeyAttr::LogSection::SOURCE ); - telemetryMap[asset_id]->template registerContext( - "assetId", - asset_id, - EnvKeyAttr::LogSection::SOURCE - ); - telemetryMap[asset_id]->template registerContext( - "assetName", - data.assetName, - EnvKeyAttr::LogSection::SOURCE - ); - telemetryMap[asset_id]->template registerContext( - "practiceId", - data.practiceId, - EnvKeyAttr::LogSection::SOURCE - ); - telemetryMap[asset_id]->template registerContext( - "practiceName", - data.practiceName, - EnvKeyAttr::LogSection::SOURCE - ); - telemetryMap[asset_id]->registerListener(); } + dbgTrace(D_WAAP) << "updating telemetry data for asset: " << data.assetName; + + telemetryMap[asset_id]->template registerContext( + "assetId", + asset_id, + EnvKeyAttr::LogSection::SOURCE + ); + telemetryMap[asset_id]->template registerContext( + "assetName", + data.assetName, + EnvKeyAttr::LogSection::SOURCE + ); + telemetryMap[asset_id]->template registerContext( + "practiceId", + data.practiceId, + EnvKeyAttr::LogSection::SOURCE + ); + telemetryMap[asset_id]->template registerContext( + "practiceName", + data.practiceName, + EnvKeyAttr::LogSection::SOURCE + ); } }; diff --git a/components/include/waap.h b/components/include/waap.h index 4df31ba..02f3983 100755 --- a/components/include/waap.h +++ b/components/include/waap.h @@ -33,6 +33,7 @@ class I_WaapAssetStatesManager; class I_Messaging; class I_AgentDetails; class I_Encryptor; +class I_WaapModelResultLogger; const std::string WAAP_APPLICATION_NAME = "waap application"; @@ -50,7 +51,8 @@ class WaapComponent Singleton::Consume, Singleton::Consume, Singleton::Consume, - Singleton::Consume + Singleton::Consume, + Singleton::Consume { public: WaapComponent(); 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 947b0eb..a10a17f 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 @@ -497,7 +497,8 @@ WebAppSection::WebAppSection( const AppsecPracticeAntiBotSection &_anti_bots, const LogTriggerSection &parsed_log_trigger, const AppSecTrustedSources &parsed_trusted_sources, - const NewAppSecWebAttackProtections &protections) + const NewAppSecWebAttackProtections &protections, + const vector &exceptions) : application_urls(_application_urls), asset_id(_asset_id), @@ -541,6 +542,10 @@ WebAppSection::WebAppSection( overrides.push_back(AppSecOverride(source_ident)); } + for (const auto &exception : exceptions) { + overrides.push_back(AppSecOverride(exception)); + } + } // LCOV_EXCL_STOP 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 f45b0eb..479a47b 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 @@ -298,7 +298,8 @@ public: const AppsecPracticeAntiBotSection &_anti_bots, const LogTriggerSection &parsed_log_trigger, const AppSecTrustedSources &parsed_trusted_sources, - const NewAppSecWebAttackProtections &protections); + const NewAppSecWebAttackProtections &protections, + const std::vector &exceptions); void save(cereal::JSONOutputArchive &out_ar) const; diff --git a/components/security_apps/local_policy_mgmt_gen/include/policy_activation_data.h b/components/security_apps/local_policy_mgmt_gen/include/policy_activation_data.h index e765227..d013430 100755 --- a/components/security_apps/local_policy_mgmt_gen/include/policy_activation_data.h +++ b/components/security_apps/local_policy_mgmt_gen/include/policy_activation_data.h @@ -45,7 +45,6 @@ public: private: std::string name; - std::string mode; std::vector hosts; }; 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 index 3c510eb..2e13d98 100644 --- 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 @@ -206,7 +206,8 @@ private: const RulesConfigRulebase& rule_config, const std::string &practice_id, const std::string &full_url, const std::string &default_mode, - std::map &rule_annotations + std::map &rule_annotations, + std::vector ); void 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 27cd389..404b65d 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 @@ -698,8 +698,12 @@ K8sPolicyUtils::createAppsecPolicies() } } - auto maybe_policy_activation = - getObjectFromCluster("/apis/openappsec.io/v1beta2/policyactivations"); + + string ns_suffix = getAppSecScopeType() == "namespaced" ? "ns" : ""; + string ns = getAppSecScopeType() == "namespaced" ? "namespaces/" : ""; + auto maybe_policy_activation = getObjectFromCluster( + "/apis/openappsec.io/v1beta2/" + ns + agent_ns + "policyactivations" + ns_suffix + ); if (!maybe_policy_activation.ok()) { dbgWarning(D_LOCAL_POLICY) diff --git a/components/security_apps/local_policy_mgmt_gen/new_trusted_sources.cc b/components/security_apps/local_policy_mgmt_gen/new_trusted_sources.cc index d9d2866..d64e886 100755 --- a/components/security_apps/local_policy_mgmt_gen/new_trusted_sources.cc +++ b/components/security_apps/local_policy_mgmt_gen/new_trusted_sources.cc @@ -69,7 +69,7 @@ Identifier::load(cereal::JSONInputArchive &archive_in) dbgWarning(D_LOCAL_POLICY) << "AppSec identifier invalid: " << identifier; identifier = "sourceip"; } - parseMandatoryAppsecJSONKey>("value", value, archive_in); + parseAppsecJSONKey>("value", value, archive_in); } const string & diff --git a/components/security_apps/local_policy_mgmt_gen/policy_activation_data.cc b/components/security_apps/local_policy_mgmt_gen/policy_activation_data.cc index 9b64b5c..25502e0 100755 --- a/components/security_apps/local_policy_mgmt_gen/policy_activation_data.cc +++ b/components/security_apps/local_policy_mgmt_gen/policy_activation_data.cc @@ -18,14 +18,6 @@ using namespace std; USE_DEBUG_FLAG(D_LOCAL_POLICY); -static const set valid_modes = { - "prevent-learn", - "detect-learn", - "prevent", - "detect", - "inactive" -}; - void PolicyActivationMetadata::load(cereal::JSONInputArchive &archive_in) { @@ -39,11 +31,6 @@ EnabledPolicy::load(cereal::JSONInputArchive &archive_in) dbgTrace(D_LOCAL_POLICY) << "Loading policyActivation enabled policy"; parseMandatoryAppsecJSONKey>("hosts", hosts, archive_in); parseAppsecJSONKey("name", name, archive_in); - parseAppsecJSONKey("mode", mode, archive_in, "detect"); - if (valid_modes.count(mode) == 0) { - dbgWarning(D_LOCAL_POLICY) << "AppSec policy activation mode invalid: " << mode; - mode = "detect"; - } } const string & 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 7dcf984..3c4560f 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 @@ -928,7 +928,6 @@ createMultiRulesSections( PracticeSection practice = PracticeSection(practice_id, practice_type, practice_name); vector exceptions_result; for (auto exception : exceptions) { - const auto &exception_name = exception.first; for (const auto &inner_exception : exception.second) { exceptions_result.push_back(ParametersSection(inner_exception.getBehaviorId(), exception_name)); @@ -1220,7 +1219,8 @@ PolicyMakerUtils::createWebAppSection( const string &practice_id, const string &full_url, const string &default_mode, - map &rule_annotations) + map &rule_annotations, + vector rule_inner_exceptions) { auto apssec_practice = getAppsecPracticeSpec( @@ -1255,7 +1255,8 @@ PolicyMakerUtils::createWebAppSection( apssec_practice.getAntiBot(), log_triggers[rule_annotations[AnnotationTypes::TRIGGER]], trusted_sources[rule_annotations[AnnotationTypes::TRUSTED_SOURCES]], - apssec_practice.getWebAttacks().getProtections() + apssec_practice.getWebAttacks().getProtections(), + rule_inner_exceptions ); web_apps[rule_config.getAssetName()] = web_app; } @@ -1366,7 +1367,8 @@ PolicyMakerUtils::createThreatPreventionPracticeSections( practice_id, asset_name, default_mode, - rule_annotations); + rule_annotations, + inner_exceptions[rule_annotations[AnnotationTypes::EXCEPTION]]); } } diff --git a/components/security_apps/orchestration/manifest_controller/manifest_controller.cc b/components/security_apps/orchestration/manifest_controller/manifest_controller.cc index dfc7870..f42d5af 100755 --- a/components/security_apps/orchestration/manifest_controller/manifest_controller.cc +++ b/components/security_apps/orchestration/manifest_controller/manifest_controller.cc @@ -100,6 +100,7 @@ private: string packages_dir; string orch_service_name; set ignore_packages; + Maybe forbidden_versions = genError("Forbidden versions file does not exist"); }; void @@ -135,7 +136,8 @@ ManifestController::Impl::init() "Ignore packages list file path" ); - if (Singleton::Consume::by()->doesFileExist(ignore_packages_path)) { + auto orchestration_tools = Singleton::Consume::by(); + if (orchestration_tools->doesFileExist(ignore_packages_path)) { try { ifstream input_stream(ignore_packages_path); if (!input_stream) { @@ -156,6 +158,9 @@ ManifestController::Impl::init() << " Error: " << f.what(); } } + + const string forbidden_versions_path = getFilesystemPathConfig() + "/revert/forbidden_versions"; + forbidden_versions = orchestration_tools->readFile(forbidden_versions_path); } bool @@ -271,6 +276,17 @@ ManifestController::Impl::updateManifest(const string &new_manifest_file) } map new_packages = parsed_manifest.unpack(); + if (!new_packages.empty()) { + const Package &package = new_packages.begin()->second; + if (forbidden_versions.ok() && + forbidden_versions.unpack().find(package.getVersion()) != string::npos + ) { + dbgWarning(D_ORCHESTRATOR) + << "Packages version is in the forbidden versions list. No upgrade will be performed."; + return true; + } + } + map all_packages = parsed_manifest.unpack(); map current_packages; parsed_manifest = orchestration_tools->loadPackagesFromJson(manifest_file_path); diff --git a/components/security_apps/orchestration/manifest_controller/manifest_controller_ut/manifest_controller_ut.cc b/components/security_apps/orchestration/manifest_controller/manifest_controller_ut/manifest_controller_ut.cc index 4a04eb8..5f8b1ce 100755 --- a/components/security_apps/orchestration/manifest_controller/manifest_controller_ut/manifest_controller_ut.cc +++ b/components/security_apps/orchestration/manifest_controller/manifest_controller_ut/manifest_controller_ut.cc @@ -58,6 +58,9 @@ public: Debug::setUnitTestFlag(D_ORCHESTRATOR, Debug::DebugLevel::TRACE); const string ignore_packages_file = "/etc/cp/conf/ignore-packages.txt"; EXPECT_CALL(mock_orchestration_tools, doesFileExist(ignore_packages_file)).WillOnce(Return(false)); + Maybe forbidden_versions(string("a1\na2")); + EXPECT_CALL(mock_orchestration_tools, readFile("/etc/cp/revert/forbidden_versions")) + .WillOnce(Return(forbidden_versions)); manifest_controller.init(); manifest_file_path = getConfigurationWithDefault( "/etc/cp/conf/manifest.json", @@ -224,6 +227,10 @@ TEST_F(ManifestControllerTest, createNewManifest) EXPECT_CALL(mock_orchestration_tools, copyFile(file_name, manifest_file_path)).WillOnce(Return(true)); EXPECT_CALL(mock_orchestration_tools, isNonEmptyFile(manifest_file_path)).WillOnce(Return(true)); EXPECT_CALL(mock_orchestration_tools, removeFile(file_name)).WillOnce(Return(true)); + EXPECT_CALL(mock_details_resolver, getAgentVersion()).WillOnce(Return("b")); + EXPECT_CALL(mock_orchestration_tools, doesFileExist("/etc/cp/revert/upgrade_status")).WillOnce(Return(false)); + EXPECT_CALL(mock_orchestration_tools, writeFile(_, "/etc/cp/revert/upgrade_status", false)) + .WillOnce(Return(true)); EXPECT_TRUE(i_manifest_controller->updateManifest(file_name)); } @@ -363,6 +370,11 @@ TEST_F(ManifestControllerTest, updateManifest) EXPECT_CALL(mock_orchestration_tools, isNonEmptyFile(manifest_file_path)).Times(2).WillRepeatedly(Return(true)); EXPECT_CALL(mock_orchestration_tools, removeFile(file_name)).Times(2).WillRepeatedly(Return(true)); + EXPECT_CALL(mock_details_resolver, getAgentVersion()).WillOnce(Return("b")); + EXPECT_CALL(mock_orchestration_tools, doesFileExist("/etc/cp/revert/upgrade_status")).WillOnce(Return(false)); + EXPECT_CALL(mock_orchestration_tools, writeFile(_, "/etc/cp/revert/upgrade_status", false)) + .WillOnce(Return(true)); + EXPECT_TRUE(i_manifest_controller->updateManifest(file_name)); manifest = @@ -417,6 +429,9 @@ TEST_F(ManifestControllerTest, updateManifest) EXPECT_CALL(mock_orchestration_tools, loadPackagesFromJson(file_name)).WillOnce(Return(new_services)); EXPECT_CALL(mock_orchestration_tools, loadPackagesFromJson(manifest_file_path)).WillOnce(Return(old_services)); + + EXPECT_CALL(mock_details_resolver, getAgentVersion()).WillOnce(Return("b")); + EXPECT_CALL(mock_orchestration_tools, doesFileExist("/etc/cp/revert/upgrade_status")).WillRepeatedly(Return(true)); EXPECT_TRUE(i_manifest_controller->updateManifest(file_name)); } @@ -478,6 +493,11 @@ TEST_F(ManifestControllerTest, selfUpdate) EXPECT_CALL(mock_orchestration_tools, copyFile("/tmp/temp_file", path + temp_ext)).WillOnce(Return(true)); + + EXPECT_CALL(mock_details_resolver, getAgentVersion()).WillOnce(Return("b")); + EXPECT_CALL(mock_orchestration_tools, doesFileExist("/etc/cp/revert/upgrade_status")).WillOnce(Return(false)); + EXPECT_CALL(mock_orchestration_tools, writeFile(_, "/etc/cp/revert/upgrade_status", false)) + .WillOnce(Return(true)); EXPECT_TRUE(i_manifest_controller->updateManifest(file_name)); } @@ -607,6 +627,10 @@ TEST_F(ManifestControllerTest, removeCurrentErrorPackage) EXPECT_CALL(mock_orchestration_tools, isNonEmptyFile(manifest_file_path)).WillOnce(Return(true)); EXPECT_CALL(mock_orchestration_tools, removeFile(file_name)).WillOnce(Return(true)); + EXPECT_CALL(mock_details_resolver, getAgentVersion()).WillOnce(Return("b")); + EXPECT_CALL(mock_orchestration_tools, doesFileExist("/etc/cp/revert/upgrade_status")).WillOnce(Return(false)); + EXPECT_CALL(mock_orchestration_tools, writeFile(_, "/etc/cp/revert/upgrade_status", false)) + .WillOnce(Return(true)); corrupted_packages.clear(); EXPECT_TRUE(i_manifest_controller->updateManifest(file_name)); } @@ -666,6 +690,10 @@ TEST_F(ManifestControllerTest, selfUpdateWithOldCopy) EXPECT_CALL(mock_orchestration_tools, copyFile("/tmp/temp_file", path + temp_ext)).WillOnce(Return(true)); + EXPECT_CALL(mock_details_resolver, getAgentVersion()).WillOnce(Return("b")); + EXPECT_CALL(mock_orchestration_tools, doesFileExist("/etc/cp/revert/upgrade_status")).WillOnce(Return(false)); + EXPECT_CALL(mock_orchestration_tools, writeFile(_, "/etc/cp/revert/upgrade_status", false)) + .WillOnce(Return(true)); EXPECT_TRUE(i_manifest_controller->updateManifest(file_name)); } @@ -722,6 +750,10 @@ TEST_F(ManifestControllerTest, selfUpdateWithOldCopyWithError) EXPECT_CALL(mock_orchestration_tools, doesFileExist(path)).WillOnce(Return(false)).WillOnce(Return(true)); EXPECT_CALL(mock_orchestration_tools, copyFile(path, path + backup_ext + temp_ext)).WillOnce(Return(false)); EXPECT_CALL(mock_details_resolver, getHostname()).WillOnce(Return(hostname)); + EXPECT_CALL(mock_details_resolver, getAgentVersion()).WillOnce(Return("b")); + EXPECT_CALL(mock_orchestration_tools, doesFileExist("/etc/cp/revert/upgrade_status")).WillOnce(Return(false)); + EXPECT_CALL(mock_orchestration_tools, writeFile(_, "/etc/cp/revert/upgrade_status", false)) + .WillOnce(Return(true)); EXPECT_FALSE(i_manifest_controller->updateManifest(file_name)); } @@ -798,6 +830,10 @@ TEST_F(ManifestControllerTest, installAndRemove) EXPECT_CALL(mock_orchestration_tools, isNonEmptyFile(manifest_file_path)).Times(2).WillRepeatedly(Return(true)); EXPECT_CALL(mock_orchestration_tools, removeFile(file_name)).Times(2).WillRepeatedly(Return(true)); + EXPECT_CALL(mock_details_resolver, getAgentVersion()).WillOnce(Return("b")); + EXPECT_CALL(mock_orchestration_tools, doesFileExist("/etc/cp/revert/upgrade_status")).WillOnce(Return(false)); + EXPECT_CALL(mock_orchestration_tools, writeFile(_, "/etc/cp/revert/upgrade_status", false)) + .WillOnce(Return(true)); EXPECT_TRUE(i_manifest_controller->updateManifest(file_name)); string new_manifest = @@ -858,6 +894,63 @@ TEST_F(ManifestControllerTest, installAndRemove) .WillOnce(Return(true)); EXPECT_CALL(mock_orchestration_tools, doesFileExist("/etc/cp/packages/my1/my1")).Times(2) .WillOnce(Return(false)); + EXPECT_CALL(mock_details_resolver, getAgentVersion()).WillOnce(Return("b")); + EXPECT_CALL(mock_orchestration_tools, doesFileExist("/etc/cp/revert/upgrade_status")).WillRepeatedly(Return(true)); + EXPECT_TRUE(i_manifest_controller->updateManifest(file_name)); +} + +TEST_F(ManifestControllerTest, manifestWithForbiddenVersion) +{ + new_services.clear(); + old_services.clear(); + + string manifest = + "{" + " \"packages\": [" + " {" + " \"download-path\": \"http://172.23.92.135/my.sh\"," + " \"relative-path\": \"\"," + " \"name\": \"my\"," + " \"version\": \"a1\"," + " \"checksum-type\": \"sha1sum\"," + " \"checksum\": \"a58bbab8020b0e6d08568714b5e582a3adf9c805\"," + " \"package-type\": \"service\"," + " \"require\": []" + " }," + " {" + " \"download-path\": \"http://172.23.92.135/my.sh\"," + " \"relative-path\": \"\"," + " \"name\": \"orchestration\"," + " \"version\": \"a1\"," + " \"checksum-type\": \"sha1sum\"," + " \"checksum\": \"a58bbab8020b0e6d08568714b5e582a3adf9c805\"," + " \"package-type\": \"service\"," + " \"require\": []" + " }," + " {" + " \"download-path\": \"\"," + " \"relative-path\": \"\"," + " \"name\": \"waap\"," + " \"version\": \"a1\"," + " \"checksum-type\": \"sha1sum\"," + " \"checksum\": \"\"," + " \"package-type\": \"service\"," + " \"status\": false,\n" + " \"message\": \"This security app isn't valid for this agent\"\n" + " }" + " ]" + "}"; + + map manifest_services; + load(manifest, manifest_services); + checkIfFileExistsCall(manifest_services.at("my")); + + + load(manifest, new_services); + load(old_manifest, old_services); + + EXPECT_CALL(mock_orchestration_tools, loadPackagesFromJson(file_name)).WillOnce(Return(new_services)); + EXPECT_TRUE(i_manifest_controller->updateManifest(file_name)); } @@ -947,6 +1040,10 @@ TEST_F(ManifestControllerTest, badInstall) EXPECT_CALL(mock_orchestration_tools, packagesToJsonFile(corrupted_packages, corrupted_file_list)).WillOnce(Return(true)); + EXPECT_CALL(mock_details_resolver, getAgentVersion()).WillOnce(Return("b")); + EXPECT_CALL(mock_orchestration_tools, doesFileExist("/etc/cp/revert/upgrade_status")).WillOnce(Return(false)); + EXPECT_CALL(mock_orchestration_tools, writeFile(_, "/etc/cp/revert/upgrade_status", false)) + .WillOnce(Return(true)); EXPECT_FALSE(i_manifest_controller->updateManifest(file_name)); } @@ -1112,6 +1209,12 @@ TEST_F(ManifestControllerTest, requireUpdate) .WillOnce(Return(true)); EXPECT_CALL(mock_orchestration_tools, isNonEmptyFile(manifest_file_path)).WillOnce(Return(true)); EXPECT_CALL(mock_orchestration_tools, removeFile("new_manifest.json")).WillOnce(Return(true)); + EXPECT_CALL(mock_details_resolver, getAgentVersion()).WillRepeatedly(Return("b")); + EXPECT_CALL(mock_orchestration_tools, doesFileExist("/etc/cp/revert/upgrade_status")) + .WillOnce(Return(false)) + .WillRepeatedly(Return(true));; + EXPECT_CALL(mock_orchestration_tools, writeFile(_, "/etc/cp/revert/upgrade_status", false)) + .WillOnce(Return(true)); EXPECT_TRUE(i_manifest_controller->updateManifest(file_name)); } @@ -1212,6 +1315,10 @@ TEST_F(ManifestControllerTest, sharedObjectNotInstalled) ).WillOnce(Return(true)); EXPECT_CALL(mock_orchestration_tools, copyFile("/tmp/temp_file1", path + temp_ext)).WillOnce(Return(true)); + EXPECT_CALL(mock_details_resolver, getAgentVersion()).WillOnce(Return("b")); + EXPECT_CALL(mock_orchestration_tools, doesFileExist("/etc/cp/revert/upgrade_status")).WillOnce(Return(false)); + EXPECT_CALL(mock_orchestration_tools, writeFile(_, "/etc/cp/revert/upgrade_status", false)) + .WillOnce(Return(true)); EXPECT_TRUE(i_manifest_controller->updateManifest(file_name)); } @@ -1313,6 +1420,12 @@ TEST_F(ManifestControllerTest, requireSharedObjectUpdate) .WillOnce(Return(true)); EXPECT_CALL(mock_orchestration_tools, removeFile("new_manifest.json")) .WillOnce(Return(true)); + EXPECT_CALL(mock_details_resolver, getAgentVersion()).WillRepeatedly(Return("b")); + EXPECT_CALL(mock_orchestration_tools, doesFileExist("/etc/cp/revert/upgrade_status")) + .WillOnce(Return(false)) + .WillRepeatedly(Return(true));; + EXPECT_CALL(mock_orchestration_tools, writeFile(_, "/etc/cp/revert/upgrade_status", false)) + .WillOnce(Return(true)); EXPECT_TRUE(i_manifest_controller->updateManifest(file_name)); } @@ -1389,6 +1502,7 @@ TEST_F(ManifestControllerTest, failureOnDownloadSharedObject) EXPECT_CALL(mock_details_resolver, getHostname()).WillOnce(Return(string("hostname"))); EXPECT_CALL(mock_orchestration_tools, removeFile("/tmp/temp_file1")).WillOnce(Return(true)); + EXPECT_CALL(mock_details_resolver, getAgentVersion()).WillRepeatedly(Return("b")); EXPECT_FALSE(i_manifest_controller->updateManifest(file_name)); } @@ -1524,6 +1638,12 @@ TEST_F(ManifestControllerTest, multiRequireUpdate) .WillOnce(Return(true)); EXPECT_CALL(mock_orchestration_tools, removeFile("new_manifest.json")) .WillOnce(Return(true)); + EXPECT_CALL(mock_details_resolver, getAgentVersion()).WillRepeatedly(Return("b")); + EXPECT_CALL(mock_orchestration_tools, doesFileExist("/etc/cp/revert/upgrade_status")) + .WillOnce(Return(false)) + .WillRepeatedly(Return(true));; + EXPECT_CALL(mock_orchestration_tools, writeFile(_, "/etc/cp/revert/upgrade_status", false)) + .WillOnce(Return(true)); EXPECT_TRUE(i_manifest_controller->updateManifest(file_name)); } @@ -1610,6 +1730,12 @@ TEST_F(ManifestControllerTest, createNewManifestWithUninstallablePackage) EXPECT_CALL(mock_orchestration_tools, isNonEmptyFile(manifest_file_path)).WillOnce(Return(true)); EXPECT_CALL(mock_orchestration_tools, removeFile(file_name)).WillOnce(Return(true)); + EXPECT_CALL(mock_details_resolver, getAgentVersion()).WillRepeatedly(Return("b")); + EXPECT_CALL(mock_orchestration_tools, doesFileExist("/etc/cp/revert/upgrade_status")) + .WillOnce(Return(false)) + .WillRepeatedly(Return(true));; + EXPECT_CALL(mock_orchestration_tools, writeFile(_, "/etc/cp/revert/upgrade_status", false)) + .WillOnce(Return(true)); EXPECT_TRUE(i_manifest_controller->updateManifest(file_name)); } @@ -1624,7 +1750,7 @@ TEST_F(ManifestControllerTest, updateUninstallPackage) " \"download-path\": \"\"," " \"relative-path\": \"\"," " \"name\": \"my\"," - " \"version\": \"\"," + " \"version\": \"c\"," " \"checksum-type\": \"sha1sum\"," " \"checksum\": \"\"," " \"package-type\": \"service\"," @@ -1721,6 +1847,11 @@ TEST_F(ManifestControllerTest, updateUninstallPackage) EXPECT_CALL(mock_orchestration_tools, loadPackagesFromJson(file_name)).WillOnce(Return(new_services)); EXPECT_CALL(mock_orchestration_tools, loadPackagesFromJson(manifest_file_path)).WillOnce(Return(old_services)); + + EXPECT_CALL(mock_details_resolver, getAgentVersion()).WillOnce(Return("b")); + EXPECT_CALL(mock_orchestration_tools, doesFileExist("/etc/cp/revert/upgrade_status")).WillOnce(Return(false)); + EXPECT_CALL(mock_orchestration_tools, writeFile(_, "/etc/cp/revert/upgrade_status", false)) + .WillOnce(Return(true)); EXPECT_TRUE(i_manifest_controller->updateManifest(file_name)); } @@ -1744,6 +1875,9 @@ public: setConfiguration(ignore_packages_file, "orchestration", "Ignore packages list file path"); writeIgnoreList(ignore_packages_file, ignore_services); EXPECT_CALL(mock_orchestration_tools, doesFileExist(ignore_packages_file)).WillOnce(Return(true)); + Maybe forbidden_versions(string("a1\na2")); + EXPECT_CALL(mock_orchestration_tools, readFile("/etc/cp/revert/forbidden_versions")) + .WillOnce(Return(forbidden_versions)); manifest_controller.init(); manifest_file_path = getConfigurationWithDefault( "/etc/cp/conf/manifest.json", @@ -1839,6 +1973,7 @@ public: StrictMock mock_status; StrictMock mock_downloader; StrictMock mock_orchestration_tools; + StrictMock mock_details_resolver; NiceMock mock_shell_cmd; ManifestController manifest_controller; @@ -2122,6 +2257,12 @@ TEST_F(ManifestControllerIgnorePakckgeTest, addIgnorePackageAndUpdateNormal) EXPECT_CALL(mock_orchestration_tools, isNonEmptyFile(manifest_file_path)).WillOnce(Return(true)); EXPECT_CALL(mock_orchestration_tools, removeFile(file_name)).WillOnce(Return(true)); + EXPECT_CALL(mock_details_resolver, getAgentVersion()).WillRepeatedly(Return("b")); + EXPECT_CALL(mock_orchestration_tools, doesFileExist("/etc/cp/revert/upgrade_status")) + .WillOnce(Return(false)) + .WillRepeatedly(Return(true));; + EXPECT_CALL(mock_orchestration_tools, writeFile(_, "/etc/cp/revert/upgrade_status", false)) + .WillOnce(Return(true)); EXPECT_TRUE(i_manifest_controller->updateManifest(file_name)); } @@ -2387,6 +2528,12 @@ TEST_F(ManifestControllerIgnorePakckgeTest, overrideIgnoredPackageFromProfileSet EXPECT_CALL(mock_orchestration_tools, isNonEmptyFile(manifest_file_path)).WillOnce(Return(true)); EXPECT_CALL(mock_orchestration_tools, removeFile(file_name)).WillOnce(Return(true)); + EXPECT_CALL(mock_details_resolver, getAgentVersion()).WillRepeatedly(Return("b")); + EXPECT_CALL(mock_orchestration_tools, doesFileExist("/etc/cp/revert/upgrade_status")) + .WillOnce(Return(false)) + .WillRepeatedly(Return(true));; + EXPECT_CALL(mock_orchestration_tools, writeFile(_, "/etc/cp/revert/upgrade_status", false)) + .WillOnce(Return(true)); EXPECT_TRUE(i_manifest_controller->updateManifest(file_name)); EXPECT_THAT(capture_debug.str(), Not(HasSubstr("Ignoring a package from the manifest. Package name: my"))); @@ -2411,6 +2558,9 @@ public: doesFileExist("/etc/cp/conf/ignore-packages.txt") ).WillOnce(Return(false)); + Maybe forbidden_versions(string("a1\na2")); + EXPECT_CALL(mock_orchestration_tools, readFile("/etc/cp/revert/forbidden_versions")) + .WillOnce(Return(forbidden_versions)); manifest_controller.init(); } diff --git a/components/security_apps/orchestration/manifest_controller/manifest_handler.cc b/components/security_apps/orchestration/manifest_controller/manifest_handler.cc index e196637..fc65480 100755 --- a/components/security_apps/orchestration/manifest_controller/manifest_handler.cc +++ b/components/security_apps/orchestration/manifest_controller/manifest_handler.cc @@ -14,6 +14,7 @@ #include "manifest_handler.h" #include +#include #include "debug.h" #include "config.h" @@ -201,18 +202,29 @@ ManifestHandler::installPackage( auto span_scope = i_env->startNewSpanScope(Span::ContextType::CHILD_OF); auto orchestration_status = Singleton::Consume::by(); + auto details_resolver = Singleton::Consume::by(); + auto orchestration_tools = Singleton::Consume::by(); + auto &package = package_downloaded_file.first; auto &package_name = package.getName(); auto &package_handler_path = package_downloaded_file.second; dbgInfo(D_ORCHESTRATOR) << "Handling package installation. Package: " << package_name; + string upgrade_info = + details_resolver->getAgentVersion() + " " + package.getVersion() + " " + getCurrentTimestamp(); + if (!orchestration_tools->doesFileExist(getFilesystemPathConfig() + "/revert/upgrade_status") && + !orchestration_tools->writeFile(upgrade_info, getFilesystemPathConfig() + "/revert/upgrade_status") + ) { + dbgWarning(D_ORCHESTRATOR) << "Failed to write to " + getFilesystemPathConfig() + "/revert/upgrade_status"; + } + if (package_name.compare(orch_service_name) == 0) { orchestration_status->writeStatusToFile(); bool self_update_status = selfUpdate(package, current_packages, package_handler_path); if (!self_update_status) { auto details = Singleton::Consume::by(); - auto hostname = Singleton::Consume::by()->getHostname(); + auto hostname = details_resolver->getHostname(); string err_hostname = (hostname.ok() ? "on host '" + *hostname : "'" + details->getAgentId()) + "'"; string install_error = "Warning: Agent/Gateway " + @@ -246,7 +258,6 @@ ManifestHandler::installPackage( return true; } string current_installation_file = packages_dir + "/" + package_name + "/" + package_name; - auto orchestration_tools = Singleton::Consume::by(); bool is_clean_installation = !orchestration_tools->doesFileExist(current_installation_file); @@ -368,3 +379,13 @@ ManifestHandler::selfUpdate( package_handler->preInstallPackage(orch_service_name, current_installation_file) && package_handler->installPackage(orch_service_name, current_installation_file, false); } + +string +ManifestHandler::getCurrentTimestamp() +{ + time_t now = time(nullptr); + tm* now_tm = localtime(&now); + char timestamp[20]; + strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", now_tm); + return string(timestamp); +} diff --git a/components/security_apps/orchestration/orchestration_comp.cc b/components/security_apps/orchestration/orchestration_comp.cc index 6774c4e..ec4d75f 100755 --- a/components/security_apps/orchestration/orchestration_comp.cc +++ b/components/security_apps/orchestration/orchestration_comp.cc @@ -55,6 +55,8 @@ USE_DEBUG_FLAG(D_ORCHESTRATOR); static string fw_last_update_time = ""; #endif // gaia || smb +static const size_t MAX_SERVER_NAME_LENGTH = 253; + class SetAgentUninstall : public ServerRest, @@ -103,6 +105,19 @@ public: << "Initializing Orchestration component, file system path prefix: " << filesystem_prefix; + int check_upgrade_success_interval = getSettingWithDefault(10, "successUpgradeInterval"); + Singleton::Consume::by()->addOneTimeRoutine( + I_MainLoop::RoutineType::Timer, + [this, check_upgrade_success_interval]() + { + Singleton::Consume::by()->yield( + std::chrono::minutes(check_upgrade_success_interval) + ); + processUpgradeCompletion(); + }, + "Orchestration successfully updated (One-Time After Interval)", + true + ); auto orch_policy = loadDefaultOrchestrationPolicy(); if (!orch_policy.ok()) { dbgWarning(D_ORCHESTRATOR) << "Failed to load Orchestration Policy. Error: " << orch_policy.getErr(); @@ -141,6 +156,113 @@ public: } private: + void + saveLastKnownOrchInfo(string curr_agent_version) + { + static const string upgrades_dir = filesystem_prefix + "/revert"; + static const string last_known_orchestrator = upgrades_dir + "/last_known_working_orchestrator"; + static const string current_orchestration_package = + filesystem_prefix + "/packages/orchestration/orchestration"; + static const string last_known_manifest = upgrades_dir + "/last_known_manifest"; + static const string current_manifest_file = getConfigurationWithDefault( + filesystem_prefix + "/conf/manifest.json", + "orchestration", + "Manifest file path" + ); + + if (!i_orchestration_tools->copyFile(current_orchestration_package, last_known_orchestrator)) { + dbgWarning(D_ORCHESTRATOR) << "Failed to copy the orchestration package to " << upgrades_dir; + } else { + dbgInfo(D_ORCHESTRATOR) << "last known orchestrator version updated to: " << curr_agent_version; + } + + if (!i_orchestration_tools->copyFile(current_manifest_file, last_known_manifest)) { + dbgWarning(D_ORCHESTRATOR) << "Failed to copy " << current_manifest_file << " to " << upgrades_dir; + } else { + dbgInfo(D_ORCHESTRATOR) << "last known manifest updated"; + } + return; + } + + void + processUpgradeCompletion() + { + if (!is_first_check_update_success) { + int check_upgrade_success_interval = getSettingWithDefault(10, "successUpgradeInterval"); + // LCOV_EXCL_START + Singleton::Consume::by()->addOneTimeRoutine( + I_MainLoop::RoutineType::Timer, + [this, check_upgrade_success_interval]() + { + Singleton::Consume::by()->yield( + std::chrono::minutes(check_upgrade_success_interval) + ); + processUpgradeCompletion(); + }, + "Orchestration successfully updated", + true + ); + // LCOV_EXCL_STOP + return; + } + + static const string upgrades_dir = filesystem_prefix + "/revert"; + static const string upgrade_status = upgrades_dir + "/upgrade_status"; + static const string last_known_orchestrator = upgrades_dir + "/last_known_working_orchestrator"; + static const string upgrade_failure_info_path = upgrades_dir + "/failed_upgrade_info"; + + I_DetailsResolver *i_details_resolver = Singleton::Consume::by(); + + bool is_upgrade_status_exist = i_orchestration_tools->doesFileExist(upgrade_status); + bool is_last_known_orchestrator_exist = i_orchestration_tools->doesFileExist(last_known_orchestrator); + + if (!is_upgrade_status_exist) { + if (!is_last_known_orchestrator_exist) { + saveLastKnownOrchInfo(i_details_resolver->getAgentVersion()); + } + return; + } + + auto maybe_upgrade_data = i_orchestration_tools->readFile(upgrade_status); + string upgrade_data, from_version, to_version; + if (maybe_upgrade_data.ok()) { + upgrade_data = maybe_upgrade_data.unpack(); + istringstream stream(upgrade_data); + stream >> from_version >> to_version; + } + i_orchestration_tools->removeFile(upgrade_status); + + if (i_orchestration_tools->doesFileExist(upgrade_failure_info_path)) { + string info = "Orchestration revert. "; + auto failure_info = i_orchestration_tools->readFile(upgrade_failure_info_path); + if (failure_info.ok()) info.append(failure_info.unpack()); + LogGen( + info, + ReportIS::Level::ACTION, + ReportIS::Audience::INTERNAL, + ReportIS::Severity::CRITICAL, + ReportIS::Priority::URGENT, + ReportIS::Tags::ORCHESTRATOR + ); + dbgError(D_ORCHESTRATOR) << + "Error in orchestration version: " << to_version << + ". Orchestration reverted to version: " << i_details_resolver->getAgentVersion(); + i_orchestration_tools->removeFile(upgrade_failure_info_path); + return; + } + + saveLastKnownOrchInfo(i_details_resolver->getAgentVersion()); + i_orchestration_tools->writeFile( + upgrade_data + "\n", + getLogFilesPathConfig() + "/nano_agent/prev_upgrades", + true + ); + dbgWarning(D_ORCHESTRATOR) << + "Upgrade process from version: " << from_version << + " to version: " << to_version << + " completed successfully"; + } + Maybe registerToTheFog() { @@ -1022,6 +1144,7 @@ private: UpdatesProcessResult::SUCCESS, UpdatesConfigType::GENERAL ).notify(); + if (!is_first_check_update_success) is_first_check_update_success = true; return Maybe(); } @@ -1389,6 +1512,8 @@ private: agent_data_report << AgentReportFieldWithLabel("userEdition", FogCommunication::getUserEdition()); + agent_data_report << make_pair("registeredServer", i_agent_details->getRegisteredServer()); + #if defined(gaia) || defined(smb) if (i_details_resolver->compareCheckpointVersion(8100, greater_equal())) { agent_data_report << AgentReportFieldWithLabel("isCheckpointVersionGER81", "true"); @@ -1403,6 +1528,7 @@ private: } else { curr_agent_data_report = agent_data_report; curr_agent_data_report.disableReportSending(); + agent_data_report << AgentReportFieldWithLabel("report_timestamp", i_time->getWalltimeStr()); } } @@ -1549,6 +1675,11 @@ private: << LogField("agentType", "Orchestration") << LogField("agentVersion", Version::get()); + string registered_server = getAttribute("registered-server", "registered_server"); + dbgTrace(D_ORCHESTRATOR) << "Registered server: " << registered_server; + if (!registered_server.empty()) { + i_agent_details->setRegisteredServer(registered_server.substr(0, MAX_SERVER_NAME_LENGTH)); + } auto mainloop = Singleton::Consume::by(); mainloop->addOneTimeRoutine( I_MainLoop::RoutineType::Offline, @@ -1629,7 +1760,7 @@ private: } } - string server_name = getAttribute("registered-server", "registered_server"); + string server_name = Singleton::Consume::by()->getRegisteredServer(); auto server = TagAndEnumManagement::convertStringToTag(server_name); if (server_name == "'SWAG'" || server_name == "'SWAG Server'") server = Tags::WEB_SERVER_SWAG; if (server.ok()) tags.insert(*server); @@ -1653,7 +1784,7 @@ private: tags ); - if (server_name != "") registration_report.addToOrigin(LogField("eventCategory", server_name)); + registration_report.addToOrigin(LogField("eventCategory", server_name)); auto email = getAttribute("email-address", "user_email"); if (email != "") registration_report << LogField("userDefinedId", email); @@ -2065,6 +2196,7 @@ private: int failure_count = 0; unsigned int sleep_interval = 0; bool is_new_success = false; + bool is_first_check_update_success = false; OrchestrationPolicy policy; UpdatesProcessReporter updates_process_reporter_listener; HybridModeMetric hybrid_mode_metric; @@ -2130,6 +2262,7 @@ OrchestrationComp::preload() registerExpectedSetting>("upgradeDay"); registerExpectedSetting("email-address"); registerExpectedSetting("registered-server"); + registerExpectedSetting("successUpgradeInterval"); registerExpectedConfigFile("orchestration", Config::ConfigFileType::Policy); registerExpectedConfigFile("registration-data", Config::ConfigFileType::Policy); } diff --git a/components/security_apps/orchestration/orchestration_ut/orchestration_multitenant_ut.cc b/components/security_apps/orchestration/orchestration_ut/orchestration_multitenant_ut.cc index 0ccbdc3..5982e63 100644 --- a/components/security_apps/orchestration/orchestration_ut/orchestration_multitenant_ut.cc +++ b/components/security_apps/orchestration/orchestration_ut/orchestration_multitenant_ut.cc @@ -89,6 +89,11 @@ public: EXPECT_CALL(mock_service_controller, isServiceInstalled("Access Control")).WillRepeatedly(Return(false)); + EXPECT_CALL( + mock_ml, + addOneTimeRoutine(_, _, "Orchestration successfully updated (One-Time After Interval)", true) + ).WillOnce(DoAll(SaveArg<1>(&upgrade_routine), Return(0))); + // This Holding the Main Routine of the Orchestration. EXPECT_CALL( mock_ml, @@ -156,6 +161,7 @@ public: runRoutine() { routine(); + upgrade_routine(); } void @@ -235,6 +241,7 @@ private: } I_MainLoop::Routine routine; + I_MainLoop::Routine upgrade_routine; I_MainLoop::Routine status_routine; }; diff --git a/components/security_apps/orchestration/orchestration_ut/orchestration_ut.cc b/components/security_apps/orchestration/orchestration_ut/orchestration_ut.cc index de26b80..c6b1c07 100755 --- a/components/security_apps/orchestration/orchestration_ut/orchestration_ut.cc +++ b/components/security_apps/orchestration/orchestration_ut/orchestration_ut.cc @@ -83,6 +83,12 @@ public: EXPECT_CALL(mock_orchestration_tools, readFile(orchestration_policy_file_path)).WillOnce(Return(response)); EXPECT_CALL(mock_status, setFogAddress(host_url)).WillRepeatedly(Return()); EXPECT_CALL(mock_orchestration_tools, setClusterId()); + + EXPECT_CALL( + mock_ml, + addOneTimeRoutine(_, _, "Orchestration successfully updated (One-Time After Interval)", true) + ).WillOnce(DoAll(SaveArg<1>(&upgrade_routine), Return(0))); + EXPECT_CALL( mock_ml, addOneTimeRoutine(I_MainLoop::RoutineType::System, _, "Orchestration runner", true) @@ -281,6 +287,12 @@ public: status_routine(); } + void + runUpgradeRoutine() + { + upgrade_routine(); + } + void preload() { @@ -359,6 +371,7 @@ private: I_MainLoop::Routine routine; I_MainLoop::Routine status_routine; + I_MainLoop::Routine upgrade_routine; }; @@ -601,14 +614,6 @@ TEST_F(OrchestrationTest, check_sending_registration_data) string version = "1"; EXPECT_CALL(mock_service_controller, getUpdatePolicyVersion()).WillOnce(ReturnRef(version)); - - EXPECT_CALL(mock_ml, yield(A())) - .WillOnce(Return()) - .WillOnce(Invoke([] (chrono::microseconds) { throw invalid_argument("stop while loop"); })); - try { - runRoutine(); - } catch (const invalid_argument& e) {} - string config_json = "{\n" " \"email-address\": \"fake@example.com\",\n" @@ -617,9 +622,19 @@ TEST_F(OrchestrationTest, check_sending_registration_data) istringstream ss(config_json); Singleton::Consume::from(config_comp)->loadConfiguration(ss); + EXPECT_CALL(mock_ml, yield(A())) + .WillOnce(Return()) + .WillOnce(Invoke([] (chrono::microseconds) { throw invalid_argument("stop while loop"); })); + try { + runRoutine(); + } catch (const invalid_argument& e) {} + + sending_routine(); EXPECT_THAT(message_body, HasSubstr("\"userDefinedId\": \"fake@example.com\"")); + EXPECT_THAT(message_body, HasSubstr("\"eventCategory\"")); + EXPECT_THAT(message_body, AnyOf(HasSubstr("\"Embedded Deployment\""), HasSubstr("\"Kubernetes Deployment\""))); EXPECT_THAT(message_body, HasSubstr("\"NGINX Server\"")); } @@ -1004,6 +1019,11 @@ TEST_F(OrchestrationTest, loadOrchestrationPolicyFromBackup) ); waitForRestCall(); + EXPECT_CALL( + mock_ml, + addOneTimeRoutine(_, _, "Orchestration successfully updated (One-Time After Interval)", true) + ); + EXPECT_CALL( mock_ml, addOneTimeRoutine(I_MainLoop::RoutineType::System, _, "Orchestration runner", true) @@ -1170,6 +1190,29 @@ TEST_F(OrchestrationTest, manifestUpdate) try { runRoutine(); } catch (const invalid_argument& e) {} + + EXPECT_CALL(mock_orchestration_tools, doesFileExist("/etc/cp/revert/upgrade_status")).WillOnce(Return(true)); + EXPECT_CALL(mock_orchestration_tools, doesFileExist("/etc/cp/revert/last_known_working_orchestrator")) + .WillOnce(Return(true)); + + Maybe upgrade_status(string("1.1.1 1.1.2 2025-01-28 07:53:23")); + EXPECT_CALL(mock_orchestration_tools, readFile("/etc/cp/revert/upgrade_status")) + .WillOnce(Return(upgrade_status)); + EXPECT_CALL(mock_orchestration_tools, removeFile("/etc/cp/revert/upgrade_status")).WillOnce(Return(true)); + + EXPECT_CALL(mock_orchestration_tools, doesFileExist("/etc/cp/revert/failed_upgrade_info")) + .WillOnce(Return(false)); + + EXPECT_CALL(mock_details_resolver, getAgentVersion()).WillRepeatedly(Return("1.1.2")); + EXPECT_CALL(mock_orchestration_tools, copyFile(_, "/etc/cp/revert/last_known_working_orchestrator")) + .WillOnce(Return(true)); + EXPECT_CALL(mock_orchestration_tools, copyFile(_, "/etc/cp/revert/last_known_manifest")).WillOnce(Return(true)); + EXPECT_CALL( + mock_orchestration_tools, + writeFile("1.1.1 1.1.2 2025-01-28 07:53:23\n", "/var/log/nano_agent/prev_upgrades", true) + ).WillOnce(Return(true)); + EXPECT_CALL(mock_ml, yield(A())).WillOnce(Return()); + runUpgradeRoutine(); } TEST_F(OrchestrationTest, getBadPolicyUpdate) diff --git a/components/security_apps/orchestration/update_communication/fog_authenticator.cc b/components/security_apps/orchestration/update_communication/fog_authenticator.cc index 8ee56d4..6c34d8d 100755 --- a/components/security_apps/orchestration/update_communication/fog_authenticator.cc +++ b/components/security_apps/orchestration/update_communication/fog_authenticator.cc @@ -377,9 +377,13 @@ FogAuthenticator::registerLocalAgentToFog() { auto local_reg_token = getRegistrationToken(); if (!local_reg_token.ok()) return; + + string reg_token = local_reg_token.unpack().getData(); + if (reg_token.empty()) return; + dbgInfo(D_ORCHESTRATOR) << "Start local agent registration to the fog"; - string exec_command = "open-appsec-ctl --set-mode --online_mode --token " + local_reg_token.unpack().getData(); + string exec_command = "open-appsec-ctl --set-mode --online_mode --token " + reg_token; auto i_agent_details = Singleton::Consume::by(); auto fog_address = i_agent_details->getFogDomain(); diff --git a/components/security_apps/waap/include/i_serialize.h b/components/security_apps/waap/include/i_serialize.h index ee59f5f..31b7d8e 100755 --- a/components/security_apps/waap/include/i_serialize.h +++ b/components/security_apps/waap/include/i_serialize.h @@ -137,9 +137,13 @@ public: void setRemoteSyncEnabled(bool enabled); protected: void mergeProcessedFromRemote(); + std::string getWindowId(); + void waitSync(); std::string getPostDataUrl(); std::string getUri(); size_t getIntervalsCount(); + void incrementIntervalsCount(); + bool isBase(); template bool sendObject(T &obj, HTTPMethod method, std::string uri) @@ -252,14 +256,13 @@ protected: const std::string m_remotePath; // Created from tenentId + / + assetId + / + class std::chrono::seconds m_interval; std::string m_owner; + const std::string m_assetId; private: bool localSyncAndProcess(); void updateStateFromRemoteService(); RemoteFilesList getProcessedFilesList(); RemoteFilesList getRemoteProcessedFilesList(); - std::string getWindowId(); - bool isBase(); std::string getLearningHost(); std::string getSharedStorageHost(); @@ -270,7 +273,6 @@ private: size_t m_windowsCount; size_t m_intervalsCounter; bool m_remoteSyncEnabled; - const std::string m_assetId; const bool m_isAssetIdUuid; std::string m_type; std::string m_lastProcessedModified; diff --git a/components/security_apps/waap/include/i_waapConfig.h b/components/security_apps/waap/include/i_waapConfig.h index 5d1340b..f44ac23 100755 --- a/components/security_apps/waap/include/i_waapConfig.h +++ b/components/security_apps/waap/include/i_waapConfig.h @@ -19,12 +19,14 @@ #include "../waap_clib/WaapParameters.h" #include "../waap_clib/WaapOpenRedirectPolicy.h" #include "../waap_clib/WaapErrorDisclosurePolicy.h" +#include "../waap_clib/DecisionType.h" #include "../waap_clib/CsrfPolicy.h" #include "../waap_clib/UserLimitsPolicy.h" #include "../waap_clib/RateLimiting.h" #include "../waap_clib/SecurityHeadersPolicy.h" #include + enum class BlockingLevel { NO_BLOCKING = 0, LOW_BLOCKING_LEVEL, @@ -44,8 +46,8 @@ public: virtual const std::string& get_AssetId() const = 0; virtual const std::string& get_AssetName() const = 0; virtual const BlockingLevel& get_BlockingLevel() const = 0; - virtual const std::string& get_PracticeId() const = 0; - virtual const std::string& get_PracticeName() const = 0; + virtual const std::string& get_PracticeIdByPactice(DecisionType practiceType) const = 0; + virtual const std::string& get_PracticeNameByPactice(DecisionType practiceType) const = 0; virtual const std::string& get_PracticeSubType() const = 0; virtual const std::string& get_RuleId() const = 0; virtual const std::string& get_RuleName() const = 0; diff --git a/components/security_apps/waap/waap_clib/CMakeLists.txt b/components/security_apps/waap/waap_clib/CMakeLists.txt index af71189..67277c5 100755 --- a/components/security_apps/waap/waap_clib/CMakeLists.txt +++ b/components/security_apps/waap/waap_clib/CMakeLists.txt @@ -91,6 +91,7 @@ add_library(waap_clib ParserScreenedJson.cc ParserBinaryFile.cc RegexComparator.cc + RequestsMonitor.cc ) add_definitions("-Wno-unused-function") diff --git a/components/security_apps/waap/waap_clib/RequestsMonitor.cc b/components/security_apps/waap/waap_clib/RequestsMonitor.cc new file mode 100644 index 0000000..af156c1 --- /dev/null +++ b/components/security_apps/waap/waap_clib/RequestsMonitor.cc @@ -0,0 +1,158 @@ +#include "RequestsMonitor.h" +#include "waap.h" +#include "SyncLearningNotification.h" +#include "report_messaging.h" +#include "customized_cereal_map.h" + +USE_DEBUG_FLAG(D_WAAP_CONFIDENCE_CALCULATOR); +using namespace std; + +SourcesRequestMonitor::SourcesRequestMonitor( + const string& filePath, + const string& remotePath, + const string& assetId, + const string& owner) : + SerializeToLocalAndRemoteSyncBase( + chrono::minutes(10), + chrono::seconds(30), + filePath, + remotePath != "" ? remotePath + "/Monitor" : remotePath, + assetId, + owner + ), m_sourcesRequests() +{ +} + +SourcesRequestMonitor::~SourcesRequestMonitor() +{ +} + +void SourcesRequestMonitor::syncWorker() +{ + dbgInfo(D_WAAP_CONFIDENCE_CALCULATOR) << "Running the sync worker for assetId='" << m_assetId << "', owner='" << + m_owner << "'"; + incrementIntervalsCount(); + OrchestrationMode mode = Singleton::exists() ? + Singleton::Consume::by()->getOrchestrationMode() : OrchestrationMode::ONLINE; + + bool enabled = getProfileAgentSettingWithDefault(false, "appsec.sourceRequestsMonitor.enabled"); + + if (mode == OrchestrationMode::OFFLINE || !enabled || isBase() || !postData()) { + dbgInfo(D_WAAP_CONFIDENCE_CALCULATOR) + << "Did not report data. for asset: " + << m_assetId + << " Remote URL: " + << m_remotePath + << " is enabled: " + << to_string(enabled) + << ", mode: " << int(mode); + return; + } + + dbgTrace(D_WAAP_CONFIDENCE_CALCULATOR) << "Waiting for all agents to post their data"; + waitSync(); + + if (mode == OrchestrationMode::HYBRID) { + dbgDebug(D_WAAP_CONFIDENCE_CALCULATOR) << "detected running in standalone mode. not sending sync notification"; + } else { + SyncLearningNotificationObject syncNotification(m_assetId, "Monitor", getWindowId()); + + dbgDebug(D_WAAP_CONFIDENCE_CALCULATOR) << "sending sync notification: " << syncNotification; + + ReportMessaging( + "sync notification for '" + m_assetId + "'", + ReportIS::AudienceTeam::WAAP, + syncNotification, + MessageCategory::GENERIC, + ReportIS::Tags::WAF, + ReportIS::Notification::SYNC_LEARNING + ); + } +} + +void SourcesRequestMonitor::logSourceHit(const string& source) +{ + m_sourcesRequests[chrono::duration_cast( + Singleton::Consume::by()->getWalltime() + ).count()][source]++; +} + +// LCOV_EXCL_START Reason: internal functions not used + +void SourcesRequestMonitor::pullData(const vector &data) +{ + // not used. report only +} + +void SourcesRequestMonitor::processData() +{ + // not used. report only +} + +void SourcesRequestMonitor::postProcessedData() +{ + // not used. report only +} + +void SourcesRequestMonitor::pullProcessedData(const vector &data) +{ + // not used. report only +} + +void SourcesRequestMonitor::updateState(const vector &data) +{ + // not used. report only +} + +// LCOV_EXCL_STOP + +typedef map> MonitorJsonData; + +class SourcesRequestsReport : public RestGetFile +{ +public: + SourcesRequestsReport(MonitorData& _sourcesRequests, const string& _agentId) + : sourcesRequests(), agentId(_agentId) + { + MonitorJsonData montiorData; + for (const auto& window : _sourcesRequests) { + for (const auto& source : window.second) { + montiorData[to_string(window.first)][source.first] = source.second; + } + } + sourcesRequests = montiorData; + } + private: + C2S_PARAM(MonitorJsonData, sourcesRequests); + C2S_PARAM(string, agentId); +}; + +bool SourcesRequestMonitor::postData() +{ + dbgInfo(D_WAAP_CONFIDENCE_CALCULATOR) << "Sending the data to remote"; + // send collected data to remote and clear the local data + string url = getPostDataUrl(); + string agentId = Singleton::Consume::by()->getAgentId(); + SourcesRequestsReport currentWindow(m_sourcesRequests, agentId); + bool ok = sendNoReplyObjectWithRetry(currentWindow, + HTTPMethod::PUT, + url); + if (!ok) { + dbgError(D_WAAP_CONFIDENCE_CALCULATOR) << "Failed to post collected data to: " << url; + } + dbgInfo(D_WAAP_CONFIDENCE_CALCULATOR) << "Data sent to remote: " << ok; + m_sourcesRequests.clear(); + return ok; +} + +void SourcesRequestMonitor::serialize(ostream& stream) +{ + cereal::JSONOutputArchive archive(stream); + archive(m_sourcesRequests); +} + +void SourcesRequestMonitor::deserialize(istream& stream) +{ + cereal::JSONInputArchive archive(stream); + archive(m_sourcesRequests); +} diff --git a/components/security_apps/waap/waap_clib/RequestsMonitor.h b/components/security_apps/waap/waap_clib/RequestsMonitor.h new file mode 100644 index 0000000..f0298eb --- /dev/null +++ b/components/security_apps/waap/waap_clib/RequestsMonitor.h @@ -0,0 +1,33 @@ +#ifndef __REQUESTS_MONITOR_H__ +#define __REQUESTS_MONITOR_H__ +#include "i_serialize.h" + +typedef std::map> MonitorData; + +class SourcesRequestMonitor : public SerializeToLocalAndRemoteSyncBase +{ +public: + SourcesRequestMonitor( + const std::string& filePath, + const std::string& remotePath, + const std::string& assetId, + const std::string& owner); + virtual ~SourcesRequestMonitor(); + virtual void syncWorker() override; + void logSourceHit(const std::string& source); +protected: + virtual void pullData(const std::vector &data) override; + virtual void processData() override; + virtual void postProcessedData() override; + virtual void pullProcessedData(const std::vector &data) override; + virtual void updateState(const std::vector &data) override; + virtual bool postData() override; + + void serialize(std::ostream& stream); + void deserialize(std::istream& stream); +private: + // map of sources and their requests per minute (UNIX) + MonitorData m_sourcesRequests; +}; + +#endif // __REQUESTS_MONITOR_H__ diff --git a/components/security_apps/waap/waap_clib/Serializator.cc b/components/security_apps/waap/waap_clib/Serializator.cc index 0933812..da4dab0 100755 --- a/components/security_apps/waap/waap_clib/Serializator.cc +++ b/components/security_apps/waap/waap_clib/Serializator.cc @@ -407,6 +407,7 @@ SerializeToLocalAndRemoteSyncBase::SerializeToLocalAndRemoteSyncBase( m_remotePath(replaceAllCopy(remotePath, "//", "/")), m_interval(0), m_owner(owner), + m_assetId(replaceAllCopy(assetId, "/", "")), m_pMainLoop(nullptr), m_waitForSync(waitForSync), m_workerRoutineId(0), @@ -414,7 +415,6 @@ SerializeToLocalAndRemoteSyncBase::SerializeToLocalAndRemoteSyncBase( m_windowsCount(0), m_intervalsCounter(0), m_remoteSyncEnabled(true), - m_assetId(replaceAllCopy(assetId, "/", "")), m_isAssetIdUuid(Waap::Util::isUuid(assetId)), m_shared_storage_host(genError("not set")), m_learning_host(genError("not set")) @@ -469,6 +469,15 @@ bool SerializeToLocalAndRemoteSyncBase::isBase() return m_remotePath == ""; } +void SerializeToLocalAndRemoteSyncBase::waitSync() +{ + if (m_pMainLoop == nullptr) + { + return; + } + m_pMainLoop->yield(m_waitForSync); +} + string SerializeToLocalAndRemoteSyncBase::getUri() { static const string hybridModeUri = "/api"; @@ -484,6 +493,11 @@ size_t SerializeToLocalAndRemoteSyncBase::getIntervalsCount() return m_intervalsCounter; } +void SerializeToLocalAndRemoteSyncBase::incrementIntervalsCount() +{ + m_intervalsCounter++; +} + SerializeToLocalAndRemoteSyncBase::~SerializeToLocalAndRemoteSyncBase() { @@ -659,7 +673,7 @@ void SerializeToLocalAndRemoteSyncBase::syncWorker() { dbgInfo(D_WAAP_CONFIDENCE_CALCULATOR) << "Running the sync worker for assetId='" << m_assetId << "', owner='" << m_owner << "'" << " last modified state: " << m_lastProcessedModified; - m_intervalsCounter++; + incrementIntervalsCount(); OrchestrationMode mode = Singleton::exists() ? Singleton::Consume::by()->getOrchestrationMode() : OrchestrationMode::ONLINE; @@ -678,7 +692,7 @@ void SerializeToLocalAndRemoteSyncBase::syncWorker() } dbgTrace(D_WAAP_CONFIDENCE_CALCULATOR) << "Waiting for all agents to post their data"; - m_pMainLoop->yield(m_waitForSync); + waitSync(); // check if learning service is operational if (m_lastProcessedModified == "") { diff --git a/components/security_apps/waap/waap_clib/Telemetry.cc b/components/security_apps/waap/waap_clib/Telemetry.cc index 447cc3a..08105f5 100755 --- a/components/security_apps/waap/waap_clib/Telemetry.cc +++ b/components/security_apps/waap/waap_clib/Telemetry.cc @@ -33,6 +33,7 @@ WaapTelemetryBase::sendLog(const LogRest &metric_client_rest) const OrchestrationMode mode = Singleton::Consume::by()->getOrchestrationMode(); GenericMetric::sendLog(metric_client_rest); + dbgTrace(D_WAAP) << "Waap telemetry log sent: " << metric_client_rest.genJson().unpack(); if (mode == OrchestrationMode::ONLINE) { return; @@ -79,7 +80,16 @@ void WaapTelemetrics::updateMetrics(const string &asset_id, const DecisionTelemetryData &data) { initMetrics(); - requests.report(1); + + auto is_keep_alive_ctx = Singleton::Consume::by()->get( + "keep_alive_request_ctx" + ); + if (!is_keep_alive_ctx.ok() || !*is_keep_alive_ctx) { + requests.report(1); + } else { + dbgTrace(D_WAAP) << "Not increasing the number of requests due to keep alive"; + } + if (sources_seen.find(data.source) == sources_seen.end()) { if (sources.getCounter() == 0) sources_seen.clear(); sources_seen.insert(data.source); @@ -274,7 +284,9 @@ WaapMetricWrapper::upon(const WaapTelemetryEvent &event) ReportIS::IssuingEngine::AGENT_CORE, chrono::minutes(LOGGING_INTERVAL_IN_MINUTES), true, - ReportIS::Audience::INTERNAL + ReportIS::Audience::INTERNAL, + false, + asset_id ); metrics[asset_id]->registerListener(); } @@ -286,7 +298,9 @@ WaapMetricWrapper::upon(const WaapTelemetryEvent &event) ReportIS::IssuingEngine::AGENT_CORE, chrono::minutes(LOGGING_INTERVAL_IN_MINUTES), true, - ReportIS::Audience::INTERNAL + ReportIS::Audience::INTERNAL, + false, + asset_id ); attack_types[asset_id]->registerListener(); } diff --git a/components/security_apps/waap/waap_clib/WaapAssetState.cc b/components/security_apps/waap/waap_clib/WaapAssetState.cc index e592a3a..4689e32 100755 --- a/components/security_apps/waap/waap_clib/WaapAssetState.cc +++ b/components/security_apps/waap/waap_clib/WaapAssetState.cc @@ -135,6 +135,7 @@ WaapAssetState::WaapAssetState(std::shared_ptr signatures, m_Signatures(signatures), m_waapDataFileName(waapDataFileName), m_assetId(assetId), + m_requestsMonitor(nullptr), scoreBuilder(this), m_rateLimitingState(nullptr), m_errorLimitingState(nullptr), @@ -152,10 +153,14 @@ WaapAssetState::WaapAssetState(std::shared_ptr signatures, I_AgentDetails* agentDetails = Singleton::Consume::by(); std::string path = agentDetails->getTenantId() + "/" + assetId; m_filtersMngr = std::make_shared(path, assetId, this); + m_requestsMonitor = std::make_shared + (getWaapDataDir() + "/monitor.data", path, assetId, "State"); } else { m_filtersMngr = std::make_shared("", "", this); + m_requestsMonitor = std::make_shared + (getWaapDataDir() + "/monitor.data", "", assetId, "State"); } // Load keyword scores - copy from ScoreBuilder updateScores(); diff --git a/components/security_apps/waap/waap_clib/WaapAssetState.h b/components/security_apps/waap/waap_clib/WaapAssetState.h index 22d730d..8a12f9f 100755 --- a/components/security_apps/waap/waap_clib/WaapAssetState.h +++ b/components/security_apps/waap/waap_clib/WaapAssetState.h @@ -33,6 +33,7 @@ #include "KeywordTypeValidator.h" #include "ScanResult.h" #include "WaapSampleValue.h" +#include "RequestsMonitor.h" enum space_stage {SPACE_SYNBOL, BR_SYMBOL, BN_SYMBOL, BRN_SEQUENCE, BNR_SEQUENCE, NO_SPACES}; @@ -67,6 +68,8 @@ public: const std::string m_assetId; + std::shared_ptr m_requestsMonitor; + ScoreBuilder scoreBuilder; std::shared_ptr m_rateLimitingState; std::shared_ptr m_errorLimitingState; @@ -90,6 +93,7 @@ public: void logIndicatorsInFilters(const std::string ¶m, Waap::Keywords::KeywordsSet& keywords, IWaf2Transaction* pTransaction); void logParamHit(Waf2ScanResult& res, IWaf2Transaction* pTransaction); + void logSourceHit(const std::string& source); void filterKeywords(const std::string ¶m, Waap::Keywords::KeywordsSet& keywords, std::vector& filteredKeywords); void clearFilterVerbose(); diff --git a/components/security_apps/waap/waap_clib/WaapConfigBase.cc b/components/security_apps/waap/waap_clib/WaapConfigBase.cc index 04122cf..f38ef1a 100755 --- a/components/security_apps/waap/waap_clib/WaapConfigBase.cc +++ b/components/security_apps/waap/waap_clib/WaapConfigBase.cc @@ -329,14 +329,37 @@ const std::string& WaapConfigBase::get_AssetName() const return m_assetName; } -const std::string& WaapConfigBase::get_PracticeId() const +const std::string& WaapConfigBase::get_PracticeIdByPactice(DecisionType practiceType) const { - return m_practiceId; + + switch (practiceType) + { + case DecisionType::AUTONOMOUS_SECURITY_DECISION: + return m_practiceId; + default: + dbgError(D_WAAP) + << "Can't find practice type for practice ID by practice: " + << practiceType + << ", return web app practice ID"; + return m_practiceId; + } + } -const std::string& WaapConfigBase::get_PracticeName() const +const std::string& WaapConfigBase::get_PracticeNameByPactice(DecisionType practiceType) const { - return m_practiceName; + switch (practiceType) + { + case DecisionType::AUTONOMOUS_SECURITY_DECISION: + return m_practiceName; + default: + dbgError(D_WAAP) + << "Can't find practice type for practice name by practice: " + << practiceType + << ", return web app practice name"; + return m_practiceName; + } + } const std::string& WaapConfigBase::get_RuleId() const diff --git a/components/security_apps/waap/waap_clib/WaapConfigBase.h b/components/security_apps/waap/waap_clib/WaapConfigBase.h index 18dea20..1104858 100755 --- a/components/security_apps/waap/waap_clib/WaapConfigBase.h +++ b/components/security_apps/waap/waap_clib/WaapConfigBase.h @@ -39,8 +39,8 @@ public: virtual const std::string& get_AssetId() const; virtual const std::string& get_AssetName() const; virtual const BlockingLevel& get_BlockingLevel() const; - virtual const std::string& get_PracticeId() const; - virtual const std::string& get_PracticeName() const; + virtual const std::string& get_PracticeIdByPactice(DecisionType practiceType) const; + virtual const std::string& get_PracticeNameByPactice(DecisionType practiceType) const; virtual const std::string& get_RuleId() const; virtual const std::string& get_RuleName() const; virtual const bool& get_WebAttackMitigation() const; diff --git a/components/security_apps/waap/waap_clib/WaapOverrideFunctor.cc b/components/security_apps/waap/waap_clib/WaapOverrideFunctor.cc index 98f6dd7..375010d 100755 --- a/components/security_apps/waap/waap_clib/WaapOverrideFunctor.cc +++ b/components/security_apps/waap/waap_clib/WaapOverrideFunctor.cc @@ -89,7 +89,7 @@ bool WaapOverrideFunctor::operator()( } else if (tagLower == "url") { for (const auto &rx : rxes) { - if (W2T_REGX_MATCH(getUriStr)) return true; + if (W2T_REGX_MATCH(getUri)) return true; } return false; } diff --git a/components/security_apps/waap/waap_clib/WaapResponseInjectReasons.cc b/components/security_apps/waap/waap_clib/WaapResponseInjectReasons.cc index c88d233..3e5d878 100644 --- a/components/security_apps/waap/waap_clib/WaapResponseInjectReasons.cc +++ b/components/security_apps/waap/waap_clib/WaapResponseInjectReasons.cc @@ -23,6 +23,7 @@ ResponseInjectReasons::ResponseInjectReasons() : csrf(false), antibot(false), +captcha(false), securityHeaders(false) { } @@ -53,6 +54,13 @@ ResponseInjectReasons::setAntibot(bool flag) antibot = flag; } +void +ResponseInjectReasons::setCaptcha(bool flag) +{ + dbgTrace(D_WAAP) << "Change ResponseInjectReasons(Captcha) " << captcha << " to " << flag; + captcha = flag; +} + void ResponseInjectReasons::setCsrf(bool flag) { @@ -74,6 +82,13 @@ ResponseInjectReasons::shouldInjectAntibot() const return antibot; } +bool +ResponseInjectReasons::shouldInjectCaptcha() const +{ + dbgTrace(D_WAAP) << "shouldInjectCaptcha():: " << captcha; + return captcha; +} + bool ResponseInjectReasons::shouldInjectCsrf() const { diff --git a/components/security_apps/waap/waap_clib/WaapResponseInjectReasons.h b/components/security_apps/waap/waap_clib/WaapResponseInjectReasons.h index af4e7ee..3a4efb6 100644 --- a/components/security_apps/waap/waap_clib/WaapResponseInjectReasons.h +++ b/components/security_apps/waap/waap_clib/WaapResponseInjectReasons.h @@ -21,14 +21,17 @@ public: void clear(); bool shouldInject() const; void setAntibot(bool flag); + void setCaptcha(bool flag); void setCsrf(bool flag); void setSecurityHeaders(bool flag); bool shouldInjectAntibot() const; + bool shouldInjectCaptcha() const; bool shouldInjectCsrf() const; bool shouldInjectSecurityHeaders() const; private: bool csrf; bool antibot; + bool captcha; bool securityHeaders; }; diff --git a/components/security_apps/waap/waap_clib/Waf2Engine.cc b/components/security_apps/waap/waap_clib/Waf2Engine.cc index ea05e19..461f4a8 100755 --- a/components/security_apps/waap/waap_clib/Waf2Engine.cc +++ b/components/security_apps/waap/waap_clib/Waf2Engine.cc @@ -1098,6 +1098,7 @@ void Waf2Transaction::end_request_hdrs() { // but the State itself is not needed now Waap::Override::State overrideState = getOverrideState(m_siteConfig); } + m_pWaapAssetState->m_requestsMonitor->logSourceHit(m_source_identifier); IdentifiersEvent ids(m_source_identifier, m_pWaapAssetState->m_assetId); ids.notify(); // Read relevant headers and extract meta information such as host name @@ -1421,6 +1422,15 @@ Waf2Transaction::completeInjectionResponseBody(std::string& strInjection) m_responseInjectReasons.setAntibot(false); } + if(m_responseInjectReasons.shouldInjectCaptcha()) { + dbgTrace(D_WAAP_BOT_PROTECTION) << + "Waf2Transaction::completeInjectionResponseBody(): Injecting data (captcha)"; + //todo add captcha script + strInjection += ""; + // No need to inject more than once + m_responseInjectReasons.setCaptcha(false); + } + if (m_responseInjectReasons.shouldInjectCsrf()) { dbgTrace(D_WAAP) << "Waf2Transaction::completeInjectionResponseBody(): Injecting data (csrf)"; strInjection += ""; @@ -1567,7 +1577,7 @@ Waf2Transaction::decideFinal( dbgTrace(D_WAAP) << "Waf2Transaction::decideFinal(): got relevant API configuration from the I/S"; sitePolicy = &ngenAPIConfig; m_overrideState = getOverrideState(sitePolicy); - + shouldBlock = (getUserLimitVerdict() == ngx_http_cp_verdict_e::TRAFFIC_VERDICT_DROP); } else if (WaapConfigApplication::getWaapSiteConfig(ngenSiteConfig)) { dbgTrace(D_WAAP) << "Waf2Transaction::decideFinal(): got relevant Application configuration from the I/S"; @@ -1646,7 +1656,9 @@ void Waf2Transaction::appendCommonLogFields(LogGen& waapLog, const std::shared_ptr &triggerLog, bool shouldBlock, const std::string& logOverride, - const std::string& incidentType) const + const std::string& incidentType, + const std::string& practiceID, + const std::string& practiceName) const { auto env = Singleton::Consume::by(); auto active_id = env->get("ActiveTenantId"); @@ -1737,8 +1749,8 @@ void Waf2Transaction::appendCommonLogFields(LogGen& waapLog, waapLog << LogField("practiceType", "Threat Prevention"); waapLog << LogField("practiceSubType", m_siteConfig->get_PracticeSubType()); waapLog << LogField("ruleName", m_siteConfig->get_RuleName()); - waapLog << LogField("practiceId", m_siteConfig->get_PracticeId()); - waapLog << LogField("practiceName", m_siteConfig->get_PracticeName()); + waapLog << LogField("practiceId", practiceID); + waapLog << LogField("practiceName", practiceName); waapLog << LogField("waapIncidentType", incidentType); // Registering this value would append the list of matched override IDs to the unified log @@ -1805,8 +1817,8 @@ Waf2Transaction::sendLog() telemetryData.source = getSourceIdentifier(); telemetryData.assetName = m_siteConfig->get_AssetName(); - telemetryData.practiceId = m_siteConfig->get_PracticeId(); - telemetryData.practiceName = m_siteConfig->get_PracticeName(); + telemetryData.practiceId = m_siteConfig->get_PracticeIdByPactice(AUTONOMOUS_SECURITY_DECISION); + telemetryData.practiceName = m_siteConfig->get_PracticeNameByPactice(AUTONOMOUS_SECURITY_DECISION); if (m_scanResult) { telemetryData.attackTypes = m_scanResult->attack_types; } @@ -1947,7 +1959,11 @@ Waf2Transaction::sendLog() shouldBlock); LogGen& waap_log = logGenWrapper.getLogGen(); - appendCommonLogFields(waap_log, triggerLog, shouldBlock, logOverride, incidentType); + appendCommonLogFields( + waap_log, triggerLog, shouldBlock, logOverride, incidentType, + m_siteConfig->get_PracticeIdByPactice(AUTONOMOUS_SECURITY_DECISION), + m_siteConfig->get_PracticeNameByPactice(AUTONOMOUS_SECURITY_DECISION) + ); waap_log << LogField("waapIncidentDetails", incidentDetails); waap_log << LogField("eventConfidence", "High"); break; @@ -1980,7 +1996,11 @@ Waf2Transaction::sendLog() waap_log << LogField("waapFoundIndicators", getKeywordMatchesStr(), LogFieldOption::XORANDB64); } - appendCommonLogFields(waap_log, triggerLog, shouldBlock, logOverride, incidentType); + appendCommonLogFields( + waap_log, triggerLog, shouldBlock, logOverride, incidentType, + m_siteConfig->get_PracticeIdByPactice(AUTONOMOUS_SECURITY_DECISION), + m_siteConfig->get_PracticeNameByPactice(AUTONOMOUS_SECURITY_DECISION) + ); waap_log << LogField("waapIncidentDetails", incidentDetails); break; @@ -1996,7 +2016,11 @@ Waf2Transaction::sendLog() shouldBlock); LogGen& waap_log = logGenWrapper.getLogGen(); - appendCommonLogFields(waap_log, triggerLog, shouldBlock, logOverride, "Cross Site Request Forgery"); + appendCommonLogFields( + waap_log, triggerLog, shouldBlock, logOverride, "Cross Site Request Forgery", + m_siteConfig->get_PracticeIdByPactice(AUTONOMOUS_SECURITY_DECISION), + m_siteConfig->get_PracticeNameByPactice(AUTONOMOUS_SECURITY_DECISION) + ); waap_log << LogField("waapIncidentDetails", "CSRF Attack discovered."); break; } @@ -2177,14 +2201,13 @@ Waf2Transaction::decideAutonomousSecurity( " effective overrides count: " << m_effectiveOverrideIds.size() << " learned overrides count: " << m_exceptionLearned.size(); - - bool log_all = false; const std::shared_ptr triggerPolicy = sitePolicy.get_TriggerPolicy(); if (triggerPolicy) { const std::shared_ptr triggerLog = getTriggerLog(triggerPolicy); if (triggerLog && triggerLog->webRequests) log_all = true; } + if(decision->getThreatLevel() <= ThreatLevel::THREAT_INFO && !log_all) { decision->setLog(false); } else { diff --git a/components/security_apps/waap/waap_clib/Waf2Engine.h b/components/security_apps/waap/waap_clib/Waf2Engine.h index 27f2b73..12a4e4b 100755 --- a/components/security_apps/waap/waap_clib/Waf2Engine.h +++ b/components/security_apps/waap/waap_clib/Waf2Engine.h @@ -247,7 +247,9 @@ private: const std::shared_ptr &triggerLog, bool shouldBlock, const std::string& logOverride, - const std::string& incidentType) const; + const std::string& incidentType, + const std::string& practiceID, + const std::string& practiceName) const; std::string getUserReputationStr(double relativeReputation) const; bool isTrustedSource() const; diff --git a/components/security_apps/waap/waap_clib/Waf2EngineGetters.cc b/components/security_apps/waap/waap_clib/Waf2EngineGetters.cc index 73ecfbb..1a1c1be 100755 --- a/components/security_apps/waap/waap_clib/Waf2EngineGetters.cc +++ b/components/security_apps/waap/waap_clib/Waf2EngineGetters.cc @@ -381,7 +381,11 @@ void Waf2Transaction::sendAutonomousSecurityLog( waap_log << LogField("eventConfidence", confidence); } - appendCommonLogFields(waap_log, triggerLog, shouldBlock, logOverride, attackTypes); + appendCommonLogFields( + waap_log, triggerLog, shouldBlock, logOverride, attackTypes, + m_siteConfig->get_PracticeIdByPactice(AUTONOMOUS_SECURITY_DECISION), + m_siteConfig->get_PracticeNameByPactice(AUTONOMOUS_SECURITY_DECISION) + ); std::string sampleString = getSample(); if (sampleString.length() > MAX_LOG_FIELD_SIZE) { diff --git a/core/agent_details/agent_details.cc b/core/agent_details/agent_details.cc index e459644..f88a33b 100644 --- a/core/agent_details/agent_details.cc +++ b/core/agent_details/agent_details.cc @@ -237,6 +237,12 @@ AgentDetails::getAgentId() const return agent_id; } +string +AgentDetails::getRegisteredServer() const +{ + return server; +} + Maybe AgentDetails::getProxy() const { diff --git a/core/attachments/http_configuration/http_configuration.cc b/core/attachments/http_configuration/http_configuration.cc index 9a1ddf0..18639ea 100644 --- a/core/attachments/http_configuration/http_configuration.cc +++ b/core/attachments/http_configuration/http_configuration.cc @@ -111,6 +111,8 @@ HttpAttachmentConfiguration::save(cereal::JSONOutputArchive &archive) const cereal::make_nvp("keep_alive_interval_msec", getNumericalValue("keep_alive_interval_msec")), cereal::make_nvp("min_retries_for_verdict", getNumericalValue("min_retries_for_verdict")), cereal::make_nvp("max_retries_for_verdict", getNumericalValue("max_retries_for_verdict")), + cereal::make_nvp("hold_verdict_retries", getNumericalValue("hold_verdict_retries")), + cereal::make_nvp("hold_verdict_polling_time", getNumericalValue("hold_verdict_polling_time")), cereal::make_nvp("body_size_trigger", getNumericalValue("body_size_trigger")), cereal::make_nvp("remove_server_header", getNumericalValue("remove_server_header")) ); @@ -167,6 +169,8 @@ HttpAttachmentConfiguration::load(cereal::JSONInputArchive &archive) loadNumericalValue(archive, "keep_alive_interval_msec", DEFAULT_KEEP_ALIVE_INTERVAL_MSEC); loadNumericalValue(archive, "min_retries_for_verdict", 3); loadNumericalValue(archive, "max_retries_for_verdict", 15); + loadNumericalValue(archive, "hold_verdict_retries", 3); + loadNumericalValue(archive, "hold_verdict_polling_time", 1); loadNumericalValue(archive, "body_size_trigger", 200000); loadNumericalValue(archive, "remove_server_header", 0); } diff --git a/core/debug_is/debug.cc b/core/debug_is/debug.cc index d7dd483..3588853 100644 --- a/core/debug_is/debug.cc +++ b/core/debug_is/debug.cc @@ -527,7 +527,7 @@ Debug::preload() active_streams["FOG"] = make_shared(); string branch = Version::getBranch(); - if (branch == "open-source" || branch == "master" || branch.substr(0, 6) == "hotfix") { + if (branch == "master" || branch.substr(0, 6) == "hotfix") { should_assert_optional = false; } else { should_assert_optional = true; diff --git a/core/include/attachments/nginx_attachment_util.h b/core/include/attachments/nginx_attachment_util.h index 33885d4..6a2b5e9 100644 --- a/core/include/attachments/nginx_attachment_util.h +++ b/core/include/attachments/nginx_attachment_util.h @@ -42,6 +42,9 @@ unsigned int getFailOpenTimeout(); int isFailOpenHoldMode(); unsigned int getFailOpenHoldTimeout(); +unsigned int getHoldVerdictPollingTime(); +unsigned int getHoldVerdictRetries(); + unsigned int getMaxSessionsPerMinute(); int isFailOpenOnSessionLimit(); diff --git a/core/include/services_sdk/interfaces/i_agent_details.h b/core/include/services_sdk/interfaces/i_agent_details.h index eb66943..c19ff28 100644 --- a/core/include/services_sdk/interfaces/i_agent_details.h +++ b/core/include/services_sdk/interfaces/i_agent_details.h @@ -36,6 +36,7 @@ public: virtual Maybe getFogDomain() const = 0; virtual std::string getTenantId() const = 0; virtual std::string getProfileId() const = 0; + virtual std::string getRegisteredServer() const = 0; // Agent Details virtual Maybe getProxy() const = 0; @@ -43,6 +44,7 @@ public: virtual void setAgentId(const std::string &_agent_id) = 0; virtual std::string getAgentId() const = 0; virtual void setOrchestrationMode(OrchestrationMode _orchstration_mode) = 0; + virtual void setRegisteredServer(const std::string &_server) = 0; virtual OrchestrationMode getOrchestrationMode() const = 0; virtual std::string getAccessToken() const = 0; virtual void loadAccessToken() = 0; diff --git a/core/include/services_sdk/interfaces/messaging/messaging_metadata.h b/core/include/services_sdk/interfaces/messaging/messaging_metadata.h index 18277a3..a016d8f 100644 --- a/core/include/services_sdk/interfaces/messaging/messaging_metadata.h +++ b/core/include/services_sdk/interfaces/messaging/messaging_metadata.h @@ -75,9 +75,16 @@ public: port_num(_port_num), conn_flags(_conn_flags), should_buffer(_should_buffer), - is_to_fog(_is_to_fog) + is_to_fog(_is_to_fog), + should_send_access_token(true) {} + const bool & + shouldSendAccessToken() const + { + return should_send_access_token; + } + const std::string & getHostName() const { @@ -90,6 +97,12 @@ public: return port_num; } + void + setShouldSendAccessToken(const bool &_should_send_access_token) + { + should_send_access_token = _should_send_access_token; + } + void setConnectioFlag(MessageConnectionConfig flag) { @@ -300,6 +313,7 @@ private: bool is_to_fog = false; bool is_rate_limit_block = false; uint rate_limit_block_time = 0; + bool should_send_access_token = true; }; #endif // __MESSAGING_METADATA_H__ diff --git a/core/include/services_sdk/interfaces/mock/mock_agent_details.h b/core/include/services_sdk/interfaces/mock/mock_agent_details.h index e6b35e6..ce19f42 100644 --- a/core/include/services_sdk/interfaces/mock/mock_agent_details.h +++ b/core/include/services_sdk/interfaces/mock/mock_agent_details.h @@ -20,11 +20,13 @@ public: MOCK_CONST_METHOD0(getFogDomain, Maybe()); MOCK_CONST_METHOD0(getTenantId, std::string()); MOCK_CONST_METHOD0(getProfileId, std::string()); + MOCK_CONST_METHOD0(getRegisteredServer, std::string()); // Agent Details MOCK_CONST_METHOD0(getProxy, Maybe()); MOCK_METHOD1(setProxy, void(const std::string&)); MOCK_METHOD1(setAgentId, void(const std::string&)); + MOCK_METHOD1(setRegisteredServer, void(const std::string&)); MOCK_CONST_METHOD0(getAgentId, std::string()); MOCK_METHOD0(loadAccessToken, void()); MOCK_CONST_METHOD0(getAccessToken, std::string()); diff --git a/core/include/services_sdk/resources/agent_details.h b/core/include/services_sdk/resources/agent_details.h index c428c91..6b554c7 100644 --- a/core/include/services_sdk/resources/agent_details.h +++ b/core/include/services_sdk/resources/agent_details.h @@ -73,6 +73,7 @@ public: Maybe getOpenSSLDir() const; std::string getClusterId() const; OrchestrationMode getOrchestrationMode() const; + std::string getRegisteredServer() const; bool isOpenAppsecAgent() const; std::string getAccessToken() const; void loadAccessToken(); @@ -86,6 +87,7 @@ public: void setOpenSSLDir(const std::string &_openssl_dir) { openssl_dir = _openssl_dir; } void setSSLFlag(const bool _encrypted_connection) { encrypted_connection = _encrypted_connection; } void setOrchestrationMode(OrchestrationMode _orchstration_mode) { orchestration_mode = _orchstration_mode; } + void setRegisteredServer(const std::string &_server) { server = _server; } bool getSSLFlag() const { return encrypted_connection; } bool readAgentDetails(); @@ -117,6 +119,7 @@ private: uint16_t fog_port = 0; bool encrypted_connection = false; OrchestrationMode orchestration_mode = OrchestrationMode::ONLINE; + std::string server = "Unknown"; bool is_proxy_configured_via_settings = false; std::map proxies; diff --git a/core/include/services_sdk/resources/component_is/components_list_impl.h b/core/include/services_sdk/resources/component_is/components_list_impl.h index ab8907a..02f58bd 100644 --- a/core/include/services_sdk/resources/component_is/components_list_impl.h +++ b/core/include/services_sdk/resources/component_is/components_list_impl.h @@ -48,6 +48,7 @@ #include "intelligence_comp_v2.h" #include "messaging.h" #include "env_details.h" +#include "metric/metric_scraper.h" USE_DEBUG_FLAG(D_COMP_IS); @@ -216,6 +217,7 @@ class ComponentListCore Version, Buffer, ShellCmd, + MetricScraper, GenericMetric, Messaging, MainloopComponent, diff --git a/core/include/services_sdk/resources/debug_flags.h b/core/include/services_sdk/resources/debug_flags.h index 67d164c..b6bf3d6 100644 --- a/core/include/services_sdk/resources/debug_flags.h +++ b/core/include/services_sdk/resources/debug_flags.h @@ -153,6 +153,7 @@ DEFINE_FLAG(D_COMPONENT, D_ALL) DEFINE_FLAG(D_SDWAN, D_COMPONENT) DEFINE_FLAG(D_SDWAN_POLICY, D_SDWAN) DEFINE_FLAG(D_SDWAN_DATA, D_SDWAN) + DEFINE_FLAG(D_SDWAN_FEATURE_FLAG, D_SDWAN) DEFINE_FLAG(D_LOGGER_SDWAN, D_SDWAN) DEFINE_FLAG(D_SDWAN_API, D_SDWAN) DEFINE_FLAG(D_REVERSE_PROXY, D_COMPONENT) diff --git a/core/include/services_sdk/resources/generic_metric.h b/core/include/services_sdk/resources/generic_metric.h index 6765af5..87d680e 100644 --- a/core/include/services_sdk/resources/generic_metric.h +++ b/core/include/services_sdk/resources/generic_metric.h @@ -59,10 +59,11 @@ class GenericMetric Singleton::Consume, Singleton::Consume, Singleton::Consume, - public Listener + public Listener, + public Listener { public: - enum class Stream { FOG, DEBUG, PROMETHEUS, AIOPS, COUNT }; + enum class Stream { FOG, DEBUG, AIOPS, COUNT }; void init( @@ -72,7 +73,8 @@ public: std::chrono::seconds _report_interval, bool _reset, ReportIS::Audience _audience = ReportIS::Audience::INTERNAL, - bool _force_buffering = false + bool _force_buffering = false, + const std::string &_asset_id = "" ); template @@ -96,6 +98,7 @@ public: void resetMetrics(); void upon(const AllMetricEvent &) override; std::string respond(const AllMetricEvent &event) override; + std::vector respond(const MetricScrapeEvent &event) override; std::string getListenerName() const override; std::string getMetricName() const; @@ -113,9 +116,10 @@ private: friend class MetricCalc; void addCalc(MetricCalc *calc); + std::vector getPromMetricsData(); + void handleMetricStreamSending(); void generateLog(); - void generatePrometheus(); void generateDebug(); void generateAiopsLog(); @@ -127,10 +131,12 @@ private: ReportIS::Audience audience; std::chrono::seconds report_interval; std::vector calcs; + std::vector prometheus_calcs; Flags active_streams; bool reset; bool force_buffering = false; Context ctx; + std::string asset_id; }; #include "metric/counter.h" diff --git a/core/include/services_sdk/resources/metric/metric_calc.h b/core/include/services_sdk/resources/metric/metric_calc.h index 81728c3..eda3025 100644 --- a/core/include/services_sdk/resources/metric/metric_calc.h +++ b/core/include/services_sdk/resources/metric/metric_calc.h @@ -25,6 +25,9 @@ #include "customized_cereal_map.h" #include "compression_utils.h" #include "i_encryptor.h" +#include "event.h" + +USE_DEBUG_FLAG(D_METRICS); class GenericMetric; @@ -32,13 +35,35 @@ enum class MetricType { GAUGE, COUNTER }; struct PrometheusData { + template + void + serialize(Archive &ar) + { + try { + ar(cereal::make_nvp("metric_name", name)); + ar(cereal::make_nvp("metric_type", type)); + ar(cereal::make_nvp("metric_description", description)); + ar(cereal::make_nvp("labels", label)); + ar(cereal::make_nvp("value", value)); + } catch (const cereal::Exception &e) { + dbgTrace(D_METRICS) << "Error in serialize Prometheus data: " << e.what(); + } + } + std::string name; std::string type; - std::string desc; + std::string description; std::string label; std::string value; }; +class MetricScrapeEvent : public Event> +{ +public: + MetricScrapeEvent() {} + +}; + class AiopsMetricData { public: @@ -228,7 +253,10 @@ public: std::string getMetircDescription() const { return getMetadata("Description"); } std::string getMetadata(const std::string &metadata) const; virtual MetricType getMetricType() const { return MetricType::GAUGE; } - virtual std::vector getPrometheusMetrics() const; + virtual std::vector getPrometheusMetrics( + const std::string &metric_name, + const std::string &asset_id = "" + ) const; virtual float getValue() const = 0; virtual std::vector getAiopsMetrics() const; @@ -240,7 +268,10 @@ public: protected: void addMetric(GenericMetric *metric); - std::map getBasicLabels() const; + std::map getBasicLabels( + const std::string &metric_name, + const std::string &asset_id = "" + ) const; template void diff --git a/core/include/services_sdk/resources/metric/metric_map.h b/core/include/services_sdk/resources/metric/metric_map.h index e23982c..b9fb171 100644 --- a/core/include/services_sdk/resources/metric/metric_map.h +++ b/core/include/services_sdk/resources/metric/metric_map.h @@ -55,12 +55,17 @@ class MetricMap : public MetricCalc } std::vector - getPrometheusMetrics(const std::string &label, const std::string &name) const + getPrometheusMetrics( + const std::string &metric_name, + const std::string &label, + const std::string &name, + const std::string &asset_id + ) const { std::vector res; for (auto &metric : inner_map) { - auto sub_res = metric.second.getPrometheusMetrics(); + auto sub_res = metric.second.getPrometheusMetrics(metric_name, asset_id); for (auto &sub_metric : sub_res) { sub_metric.label += "," + label + "=\"" + metric.first + "\""; sub_metric.name = name; @@ -155,9 +160,9 @@ public: } std::vector - getPrometheusMetrics() const override + getPrometheusMetrics(const std::string &metric_name, const std::string &asset_id) const override { - return metric_map.getPrometheusMetrics(label, getMetricName()); + return metric_map.getPrometheusMetrics(metric_name, label, getMetricName(), asset_id); } std::vector diff --git a/core/include/services_sdk/resources/metric/metric_scraper.h b/core/include/services_sdk/resources/metric/metric_scraper.h new file mode 100644 index 0000000..1e020cd --- /dev/null +++ b/core/include/services_sdk/resources/metric/metric_scraper.h @@ -0,0 +1,45 @@ +// 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 __METRIC_SCRAPER_H__ +#define __METRIC_SCRAPER_H__ + +#include +#include +#include +#include + +#include "singleton.h" +#include "debug.h" +#include "component.h" +#include "event.h" +#include "i_rest_api.h" +#include "generic_metric.h" + +class MetricScraper + : + public Component, + Singleton::Consume +{ +public: + MetricScraper(); + ~MetricScraper(); + + void init(); + +private: + class Impl; + std::unique_ptr pimpl; +}; + +#endif // __METRIC_SCRAPER_H__ diff --git a/core/intelligence_is_v2/intelligence_comp_v2.cc b/core/intelligence_is_v2/intelligence_comp_v2.cc index 9fa13c2..7500051 100644 --- a/core/intelligence_is_v2/intelligence_comp_v2.cc +++ b/core/intelligence_is_v2/intelligence_comp_v2.cc @@ -492,9 +492,9 @@ private: return genError("Local intelligence server ip not configured"); } - auto res = sendLocalIntelligenceToLocalServer(rest_req, *server, primary_port_setting); + auto res = sendLocalIntelligenceToLocalServer(rest_req, *server, primary_port_setting, false); if (res.ok()) return res; - return sendLocalIntelligenceToLocalServer(rest_req, *server, secondary_port_setting); + return sendLocalIntelligenceToLocalServer(rest_req, *server, secondary_port_setting, false); } template @@ -502,8 +502,9 @@ private: sendLocalIntelligenceToLocalServer( const IntelligenceRest &rest_req, const string &server, - const string &port_setting - ) const + const string &port_setting, + const bool should_send_access_token = false +) const { auto port = getSetting("intelligence", port_setting); if (!port.ok()) { @@ -519,6 +520,7 @@ private: req_md.insertHeaders(getHTTPHeaders()); req_md.setConnectioFlag(MessageConnectionConfig::UNSECURE_CONN); req_md.setConnectioFlag(MessageConnectionConfig::ONE_TIME_CONN); + req_md.setShouldSendAccessToken(should_send_access_token); return sendIntelligenceRequestImpl(rest_req, req_md); } diff --git a/core/messaging/connection/connection.cc b/core/messaging/connection/connection.cc index 2be34e1..df5a272 100644 --- a/core/messaging/connection/connection.cc +++ b/core/messaging/connection/connection.cc @@ -184,6 +184,18 @@ public: establishConnection() { dbgFlow(D_CONNECTION) << "Establishing a new connection"; + // check if connection already established + I_MainLoop *i_mainloop = Singleton::Consume::by(); + while (lock) { + i_mainloop->yield(true); + } + lock = true; + auto unlock = make_scope_exit([&] () { lock = false; }); + + if (is_connected && !should_close_connection) { + dbgTrace(D_CONNECTION) << "Connection already established"; + return Maybe(); + } auto set_socket = setSocket(); if (!set_socket.ok()) { dbgWarning(D_CONNECTION) << "Failed to set socket: " << set_socket.getErr(); @@ -221,6 +233,7 @@ public: << (isOverProxy() ? ", Over proxy: " + settings.getProxyHost() + ":" + to_string(key.getPort()) : ""); active = Maybe(); should_close_connection = false; + is_connected = true; return Maybe(); } @@ -583,11 +596,13 @@ private: if (BIO_should_retry(bio.get())) return string(); + auto fd = BIO_get_fd(bio.get(), nullptr); + char error_buf[256]; ERR_error_string(ERR_get_error(), error_buf); string error = receive_len == 0 ? - "Connection closed by peer" : - "Failed to read data from BIO socket. Error: " + string(error_buf); + "Connection closed by peer (BIO fd: " + to_string(fd) + "). Error: " + string(error_buf) : + "Failed to read data from BIO socket (fd: " + to_string(fd) + "). Error: " + string(error_buf); dbgWarning(D_CONNECTION) << error; return genError(HTTPResponse(HTTPStatusCode::HTTP_UNKNOWN, error)); } @@ -621,17 +636,28 @@ private: Maybe sendAndReceiveData(const string &request, bool is_connect) { - dbgFlow(D_CONNECTION) << "Sending and receiving data"; + dbgFlow(D_CONNECTION) << "Sending and receiving data, lock: " << lock; I_MainLoop *i_mainloop = Singleton::Consume::by(); - while (lock) { + while (lock && !is_connect) { i_mainloop->yield(true); } lock = true; - auto unlock = make_scope_exit([&] () { lock = false; }); + dbgTrace(D_CONNECTION) << "acquire lock"; + auto unlock = make_scope_exit([&] () { + lock = false; + }); if (should_close_connection) { - dbgWarning(D_CONNECTION) << close_error.getBody(); - return genError(close_error); + dbgTrace(D_CONNECTION) << "reconnect in progress"; + while (lock) { + i_mainloop->yield(true); + } + if (!is_connected) { + dbgWarning(D_CONNECTION) << close_error.getBody(); + return genError(close_error); + } + dbgTrace(D_CONNECTION) << "reconnected by other routine"; + lock = true; } I_TimeGet *i_time = Singleton::Consume::by(); @@ -651,11 +677,13 @@ private: dbgTrace(D_CONNECTION) << "Sent the message, now waiting for response"; while (!http_parser.hasReachedError()) { if (i_time->getMonotonicTime() > receiving_end_time) { + is_connected = false; should_close_connection = true; return genError(receving_timeout); }; auto receieved = receiveData(); if (!receieved.ok()) { + is_connected = false; should_close_connection = true; return receieved.passErr(); } @@ -706,6 +734,7 @@ private: bool lock = false; bool should_close_connection = false; bool is_dual_auth = false; + bool is_connected = false; Maybe sni_hostname = genError("Uninitialized"); Maybe dn_host_name = genError("Uninitialized"); diff --git a/core/messaging/connection/connection_comp.cc b/core/messaging/connection/connection_comp.cc index a033d79..44111c5 100644 --- a/core/messaging/connection/connection_comp.cc +++ b/core/messaging/connection/connection_comp.cc @@ -92,12 +92,12 @@ private: << metadata.getPort(); MessageConnectionKey conn_key(metadata.getHostName(), metadata.getPort(), category); Connection conn(conn_key, metadata); + persistent_connections.emplace(conn_key, conn); const auto &external_certificate = metadata.getExternalCertificate(); if (!external_certificate.empty()) conn.setExternalCertificate(external_certificate); auto connected = conn.establishConnection(); - persistent_connections.emplace(conn_key, conn); if (!connected.ok()) { string connection_err = "Failed to establish connection. Error: " + connected.getErr(); diff --git a/core/messaging/include/http_request.h b/core/messaging/include/http_request.h index 9eeca07..e36aee3 100644 --- a/core/messaging/include/http_request.h +++ b/core/messaging/include/http_request.h @@ -30,7 +30,8 @@ public: HTTPMethod method, const std::string &uri, const std::map &headers, - const std::string &body + const std::string &body, + const bool should_send_access_token = true ); Maybe setConnectionHeaders(const Connection &conn, bool is_access_token_needed); diff --git a/core/messaging/messaging_comp/http_request.cc b/core/messaging/messaging_comp/http_request.cc index 6bded0a..be05185 100644 --- a/core/messaging/messaging_comp/http_request.cc +++ b/core/messaging/messaging_comp/http_request.cc @@ -79,7 +79,8 @@ HTTPRequest::prepareRequest( HTTPMethod method, const string &uri, const map &headers, - const string &body + const string &body, + const bool should_send_access_token ) { HTTPRequest req(method, uri, headers, body); @@ -94,6 +95,7 @@ HTTPRequest::prepareRequest( dont_add_access_token = true; dbgTrace(D_MESSAGING) << "Request is for agent authentication"; } + if (!should_send_access_token) dont_add_access_token = true; auto res = req.addAccessToken(conn, dont_add_access_token); if (!res.ok()) return res.passErr(); diff --git a/core/messaging/messaging_comp/messaging_comp.cc b/core/messaging/messaging_comp/messaging_comp.cc index c5ddb40..bb5a8fc 100644 --- a/core/messaging/messaging_comp/messaging_comp.cc +++ b/core/messaging/messaging_comp/messaging_comp.cc @@ -142,7 +142,13 @@ MessagingComp::sendMessage( metadata.insertHeaders(i_env->getCurrentHeadersMap()); } - auto req = HTTPRequest::prepareRequest(conn, method, uri, metadata.getHeaders(), body); + auto req = HTTPRequest::prepareRequest( + conn, + method, + uri, + metadata.getHeaders(), + body, + metadata.shouldSendAccessToken()); if (!req.ok()) return genError(HTTPResponse(HTTPStatusCode::HTTP_UNKNOWN, req.getErr())); auto response = i_conn->sendRequest(conn, *req); diff --git a/core/metric/CMakeLists.txt b/core/metric/CMakeLists.txt index a0f4951..e343903 100755 --- a/core/metric/CMakeLists.txt +++ b/core/metric/CMakeLists.txt @@ -1,3 +1,3 @@ -add_library(metric generic_metric.cc) +add_library(metric generic_metric.cc metric_scraper.cc) add_subdirectory(metric_ut) diff --git a/core/metric/generic_metric.cc b/core/metric/generic_metric.cc index ef64e25..271b81d 100644 --- a/core/metric/generic_metric.cc +++ b/core/metric/generic_metric.cc @@ -49,7 +49,7 @@ MetricCalc::getAiopsMetrics() const string description = getMetircDescription(); string type = getMetricType() == MetricType::GAUGE ? "Gauge" : "Counter"; - return { AiopsMetricData(name, type, units, description, getBasicLabels(), value) }; + return { AiopsMetricData(name, type, units, description, getBasicLabels(getMetricName()), value) }; } string @@ -77,7 +77,7 @@ MetricCalc::addMetric(GenericMetric *metric) } vector -MetricCalc::getPrometheusMetrics() const +MetricCalc::getPrometheusMetrics(const std::string &metric_name, const string &asset_id) const { float value = getValue(); if (isnan(value)) return {}; @@ -86,10 +86,10 @@ MetricCalc::getPrometheusMetrics() const res.name = getMetricDotName() != "" ? getMetricDotName() : getMetricName(); res.type = getMetricType() == MetricType::GAUGE ? "gauge" : "counter"; - res.desc = getMetircDescription(); + res.description = getMetircDescription(); stringstream labels; - const auto &label_pairs = getBasicLabels(); + const auto &label_pairs = getBasicLabels(metric_name, asset_id); bool first = true; for (auto &pair : label_pairs) { if (!first) labels << ','; @@ -106,7 +106,7 @@ MetricCalc::getPrometheusMetrics() const } map -MetricCalc::getBasicLabels() const +MetricCalc::getBasicLabels(const string &metric_name, const string &asset_id) const { map res; @@ -121,6 +121,9 @@ MetricCalc::getBasicLabels() const auto executable = env->get("Base Executable Name"); if (executable.ok()) res["process"] = *executable; + if (!asset_id.empty()) res["assetId"] = asset_id; + res["metricName"] = metric_name; + return res; } @@ -158,7 +161,8 @@ GenericMetric::init( chrono::seconds _report_interval, bool _reset, Audience _audience, - bool _force_buffering + bool _force_buffering, + const string &_asset_id ) { turnOnStream(Stream::FOG); @@ -173,6 +177,7 @@ GenericMetric::init( issuing_engine = _issuing_engine; audience = _audience; force_buffering = _force_buffering; + asset_id = _asset_id; i_mainloop->addRecurringRoutine( I_MainLoop::RoutineType::System, @@ -185,13 +190,13 @@ GenericMetric::init( }, "Metric Fog stream messaging for " + _metric_name ); + registerListener(); } void GenericMetric::handleMetricStreamSending() { if (active_streams.isSet(Stream::DEBUG)) generateDebug(); - if (active_streams.isSet(Stream::PROMETHEUS)) generatePrometheus(); if (active_streams.isSet(Stream::FOG)) generateLog(); if (active_streams.isSet(Stream::AIOPS)) generateAiopsLog(); @@ -237,6 +242,7 @@ void GenericMetric::addCalc(MetricCalc *calc) { calcs.push_back(calc); + prometheus_calcs.push_back(calc); } void @@ -254,6 +260,12 @@ GenericMetric::respond(const AllMetricEvent &event) return res; } +vector +GenericMetric::respond(const MetricScrapeEvent &) +{ + return getPromMetricsData(); +} + string GenericMetric::getListenerName() const { return metric_name; } void @@ -316,70 +328,19 @@ GenericMetric::generateLog() sendLog(metric_client_rest); } -class PrometheusRest : public ClientRest +vector +GenericMetric::getPromMetricsData() { - class Metric : public ClientRest - { - public: - Metric(const string &n, const string &t, const string &d, const string &l, const string &v) - : - metric_name(n), - metric_type(t), - metric_description(d), - labels(l), - value(v) - {} - - private: - C2S_PARAM(string, metric_name); - C2S_PARAM(string, metric_type); - C2S_PARAM(string, metric_description); - C2S_PARAM(string, labels); - C2S_PARAM(string, value); - }; - -public: - PrometheusRest() : metrics(vector()) {} - - void - addMetric(const vector &vec) - { - auto &metric_vec = metrics.get(); - metric_vec.reserve(vec.size()); - for (auto &metric : vec) { - metric_vec.emplace_back(metric.name, metric.type, metric.desc, "{" + metric.label + "}", metric.value); - } - } - -private: - C2S_PARAM(vector, metrics); -}; - -void -GenericMetric::generatePrometheus() -{ - if (!getProfileAgentSettingWithDefault(false, "prometheus")) return; - dbgTrace(D_METRICS) << "Generate prometheus metric"; - vector all_metrics; - for (auto &calc : calcs) { - const auto &cal_metrics = calc->getPrometheusMetrics(); - all_metrics.insert(all_metrics.end(), cal_metrics.begin(), cal_metrics.end()); + if (!getProfileAgentSettingWithDefault(false, "prometheus")) return all_metrics; + dbgTrace(D_METRICS) << "Get prometheus metrics"; + + for (auto &calc : prometheus_calcs) { + const auto &calc_prom_metrics = calc->getPrometheusMetrics(metric_name, asset_id); + all_metrics.insert(all_metrics.end(), calc_prom_metrics.begin(), calc_prom_metrics.end()); + calc->reset(); } - - PrometheusRest rest; - rest.addMetric(all_metrics); - - MessageMetadata new_config_req_md("127.0.0.1", 7465); - new_config_req_md.setConnectioFlag(MessageConnectionConfig::ONE_TIME_CONN); - new_config_req_md.setConnectioFlag(MessageConnectionConfig::UNSECURE_CONN); - Singleton::Consume::by()->sendSyncMessage( - HTTPMethod::POST, - "/add-metrics", - rest, - MessageCategory::GENERIC, - new_config_req_md - ); + return all_metrics; } void diff --git a/core/metric/metric_scraper.cc b/core/metric/metric_scraper.cc new file mode 100644 index 0000000..9d90ff1 --- /dev/null +++ b/core/metric/metric_scraper.cc @@ -0,0 +1,50 @@ +#include "metric/metric_scraper.h" + +using namespace std; + +USE_DEBUG_FLAG(D_METRICS); + +class MetricScraper::Impl +{ +public: + void + init() + { + Singleton::Consume::by()->addGetCall( + "service-metrics", + [&] () { return getAllPrometheusMetrics(); } + ); + } + + string + getAllPrometheusMetrics() + { + auto all_metrics_events_res = MetricScrapeEvent().query(); + for (auto metric_vec : all_metrics_events_res) { + for (PrometheusData metric : metric_vec) { + metric.label = "{" + metric.label + "}"; + all_metrics.emplace_back(metric); + } + } + stringstream ss; + { + cereal::JSONOutputArchive archive(ss); + archive(cereal::make_nvp("metrics", all_metrics)); + } + all_metrics.clear(); + return ss.str(); + } + +private: + vector all_metrics; +}; + +MetricScraper::MetricScraper() : Component("MetricScraper"), pimpl(make_unique()) {} + +MetricScraper::~MetricScraper() {} + +void +MetricScraper::init() +{ + pimpl->init(); +} diff --git a/core/metric/metric_ut/metric_ut.cc b/core/metric/metric_ut/metric_ut.cc index 13af6db..e951ee0 100644 --- a/core/metric/metric_ut/metric_ut.cc +++ b/core/metric/metric_ut/metric_ut.cc @@ -14,6 +14,7 @@ #include "mock/mock_instance_awareness.h" #include "config.h" #include "config_component.h" +#include "metric/metric_scraper.h" using namespace std; using namespace chrono; @@ -191,9 +192,11 @@ public: MetricTest() { EXPECT_CALL(rest, mockRestCall(RestAction::ADD, "declare-boolean-variable", _)).WillOnce(Return(true)); + env.init(); + conf.preload(); + ON_CALL(instance, getUniqueID()).WillByDefault(Return(string("87"))); ON_CALL(instance, getFamilyID()).WillByDefault(Return(string(""))); - env.init(); Debug::setNewDefaultStdout(&debug_output); Debug::setUnitTestFlag(D_METRICS, Debug::DebugLevel::TRACE); setConfiguration(true, string("metric"), string("fogMetricSendEnable")); @@ -531,9 +534,12 @@ TEST_F(MetricTest, printMetricsTest) GenericMetric::fini(); } -TEST_F(MetricTest, printPromeathus) +TEST_F(MetricTest, getPromeathusMetric) { - conf.preload(); + MetricScraper metric_scraper; + function get_metrics_func; + EXPECT_CALL(rest, addGetCall("service-metrics", _)).WillOnce(DoAll(SaveArg<1>(&get_metrics_func), Return(true))); + metric_scraper.init(); stringstream configuration; configuration << "{\"agentSettings\":[{\"key\":\"prometheus\",\"id\":\"id1\",\"value\":\"true\"}]}\n"; @@ -546,20 +552,21 @@ TEST_F(MetricTest, printPromeathus) ReportIS::AudienceTeam::AGENT_CORE, ReportIS::IssuingEngine::AGENT_CORE, seconds(5), - false + false, + ReportIS::Audience::INTERNAL, + false, + "asset id" ); cpu_mt.turnOffStream(GenericMetric::Stream::FOG); cpu_mt.turnOffStream(GenericMetric::Stream::DEBUG); - cpu_mt.turnOnStream(GenericMetric::Stream::PROMETHEUS); cpu_mt.registerListener(); CPUEvent cpu_event; cpu_event.setProcessCPU(89); cpu_event.notify(); - string message_body; - EXPECT_CALL(messaging_mock, sendSyncMessage(_, "/add-metrics", _, _, _)) - .WillOnce(DoAll(SaveArg<2>(&message_body), Return(HTTPResponse()))); + string message_body = get_metrics_func(); + routine(); string res = @@ -569,42 +576,48 @@ TEST_F(MetricTest, printPromeathus) " \"metric_name\": \"cpuMax\",\n" " \"metric_type\": \"gauge\",\n" " \"metric_description\": \"\",\n" - " \"labels\": \"{agent=\\\"Unknown\\\",id=\\\"87\\\"}\",\n" + " \"labels\": \"{agent=\\\"Unknown\\\",assetId=\\\"asset id\\\",id=\\\"87\\\"," + "metricName=\\\"CPU usage\\\"}\",\n" " \"value\": \"89\"\n" " },\n" " {\n" " \"metric_name\": \"cpuMin\",\n" " \"metric_type\": \"gauge\",\n" " \"metric_description\": \"\",\n" - " \"labels\": \"{agent=\\\"Unknown\\\",id=\\\"87\\\"}\",\n" + " \"labels\": \"{agent=\\\"Unknown\\\",assetId=\\\"asset id\\\",id=\\\"87\\\"," + "metricName=\\\"CPU usage\\\"}\",\n" " \"value\": \"89\"\n" " },\n" " {\n" " \"metric_name\": \"cpuAvg\",\n" " \"metric_type\": \"gauge\",\n" " \"metric_description\": \"\",\n" - " \"labels\": \"{agent=\\\"Unknown\\\",id=\\\"87\\\"}\",\n" + " \"labels\": \"{agent=\\\"Unknown\\\",assetId=\\\"asset id\\\",id=\\\"87\\\"," + "metricName=\\\"CPU usage\\\"}\",\n" " \"value\": \"89\"\n" " },\n" " {\n" " \"metric_name\": \"cpuCurrent\",\n" " \"metric_type\": \"gauge\",\n" " \"metric_description\": \"\",\n" - " \"labels\": \"{agent=\\\"Unknown\\\",id=\\\"87\\\"}\",\n" + " \"labels\": \"{agent=\\\"Unknown\\\",assetId=\\\"asset id\\\",id=\\\"87\\\"," + "metricName=\\\"CPU usage\\\"}\",\n" " \"value\": \"89\"\n" " },\n" " {\n" " \"metric_name\": \"cpuCounter\",\n" " \"metric_type\": \"gauge\",\n" " \"metric_description\": \"\",\n" - " \"labels\": \"{agent=\\\"Unknown\\\",id=\\\"87\\\"}\",\n" + " \"labels\": \"{agent=\\\"Unknown\\\",assetId=\\\"asset id\\\",id=\\\"87\\\"," + "metricName=\\\"CPU usage\\\"}\",\n" " \"value\": \"1\"\n" " },\n" " {\n" " \"metric_name\": \"cpuTotalCounter\",\n" " \"metric_type\": \"counter\",\n" " \"metric_description\": \"\",\n" - " \"labels\": \"{agent=\\\"Unknown\\\",id=\\\"87\\\"}\",\n" + " \"labels\": \"{agent=\\\"Unknown\\\",assetId=\\\"asset id\\\",id=\\\"87\\\"," + "metricName=\\\"CPU usage\\\"}\",\n" " \"value\": \"1\"\n" " }\n" " ]\n" @@ -613,9 +626,12 @@ TEST_F(MetricTest, printPromeathus) EXPECT_EQ(message_body, res); } -TEST_F(MetricTest, printPromeathusMultiMap) +TEST_F(MetricTest, getPromeathusMultiMap) { - conf.preload(); + MetricScraper metric_scraper; + function get_metrics_func; + EXPECT_CALL(rest, addGetCall("service-metrics", _)).WillOnce(DoAll(SaveArg<1>(&get_metrics_func), Return(true))); + metric_scraper.init(); stringstream configuration; configuration << "{\"agentSettings\":[{\"key\":\"prometheus\",\"id\":\"id1\",\"value\":\"true\"}]}\n"; @@ -628,18 +644,18 @@ TEST_F(MetricTest, printPromeathusMultiMap) ReportIS::AudienceTeam::AGENT_CORE, ReportIS::IssuingEngine::AGENT_CORE, seconds(5), - true + true, + ReportIS::Audience::INTERNAL, + false, + "asset id" ); - metric.turnOnStream(GenericMetric::Stream::PROMETHEUS); metric.registerListener(); HttpTransaction("/index.html", "GET", 10).notify(); HttpTransaction("/index2.html", "GET", 20).notify(); HttpTransaction("/index.html", "POST", 40).notify(); - string message_body; - EXPECT_CALL(messaging_mock, sendSyncMessage(_, "/add-metrics", _, _, _)) - .WillOnce(DoAll(SaveArg<2>(&message_body), Return(HTTPResponse()))); + string message_body = get_metrics_func(); routine(); string res = @@ -649,24 +665,156 @@ TEST_F(MetricTest, printPromeathusMultiMap) " \"metric_name\": \"request.total\",\n" " \"metric_type\": \"counter\",\n" " \"metric_description\": \"\",\n" - " \"labels\": \"{agent=\\\"Unknown\\\",id=\\\"87\\\"," - "method=\\\"GET\\\",url=\\\"/index.html\\\"}\",\n" + " \"labels\": \"{agent=\\\"Unknown\\\",assetId=\\\"asset id\\\",id=\\\"87\\\"," + "metricName=\\\"Bytes per URL\\\",method=\\\"GET\\\",url=\\\"/index.html\\\"}\",\n" " \"value\": \"1\"\n" " },\n" " {\n" " \"metric_name\": \"request.total\",\n" " \"metric_type\": \"counter\",\n" " \"metric_description\": \"\",\n" - " \"labels\": \"{agent=\\\"Unknown\\\",id=\\\"87\\\"," - "method=\\\"POST\\\",url=\\\"/index.html\\\"}\",\n" + " \"labels\": \"{agent=\\\"Unknown\\\",assetId=\\\"asset id\\\",id=\\\"87\\\"," + "metricName=\\\"Bytes per URL\\\",method=\\\"POST\\\",url=\\\"/index.html\\\"}\",\n" " \"value\": \"1\"\n" " },\n" " {\n" " \"metric_name\": \"request.total\",\n" " \"metric_type\": \"counter\",\n" " \"metric_description\": \"\",\n" - " \"labels\": \"{agent=\\\"Unknown\\\",id=\\\"87\\\"," - "method=\\\"GET\\\",url=\\\"/index2.html\\\"}\",\n" + " \"labels\": \"{agent=\\\"Unknown\\\",assetId=\\\"asset id\\\",id=\\\"87\\\"," + "metricName=\\\"Bytes per URL\\\",method=\\\"GET\\\",url=\\\"/index2.html\\\"}\",\n" + " \"value\": \"1\"\n" + " }\n" + " ]\n" + "}"; + + EXPECT_EQ(message_body, res); +} + +TEST_F(MetricTest, getPromeathusTwoMetrics) +{ + MetricScraper metric_scraper; + function get_metrics_func; + EXPECT_CALL(rest, addGetCall("service-metrics", _)).WillOnce(DoAll(SaveArg<1>(&get_metrics_func), Return(true))); + metric_scraper.init(); + + stringstream configuration; + configuration << "{\"agentSettings\":[{\"key\":\"prometheus\",\"id\":\"id1\",\"value\":\"true\"}]}\n"; + + EXPECT_TRUE(Singleton::Consume::from(conf)->loadConfiguration(configuration)); + + CPUMetric cpu_mt; + cpu_mt.init( + "CPU usage", + ReportIS::AudienceTeam::AGENT_CORE, + ReportIS::IssuingEngine::AGENT_CORE, + seconds(5), + false, + ReportIS::Audience::INTERNAL, + false, + "asset id" + ); + cpu_mt.turnOffStream(GenericMetric::Stream::FOG); + cpu_mt.turnOffStream(GenericMetric::Stream::DEBUG); + cpu_mt.registerListener(); + + CPUEvent cpu_event; + cpu_event.setProcessCPU(89); + cpu_event.notify(); + + UrlMetric2 metric; + metric.init( + "Bytes per URL", + ReportIS::AudienceTeam::AGENT_CORE, + ReportIS::IssuingEngine::AGENT_CORE, + seconds(5), + true, + ReportIS::Audience::INTERNAL, + false, + "asset id" + ); + metric.registerListener(); + + HttpTransaction("/index.html", "GET", 10).notify(); + HttpTransaction("/index2.html", "GET", 20).notify(); + HttpTransaction("/index.html", "POST", 40).notify(); + + string message_body = get_metrics_func(); + routine(); + + string res = + "{\n" + " \"metrics\": [\n" + " {\n" + " \"metric_name\": \"request.total\",\n" + " \"metric_type\": \"counter\",\n" + " \"metric_description\": \"\",\n" + " \"labels\": \"{agent=\\\"Unknown\\\",assetId=\\\"asset id\\\",id=\\\"87\\\"," + "metricName=\\\"Bytes per URL\\\",method=\\\"GET\\\",url=\\\"/index.html\\\"}\",\n" + " \"value\": \"1\"\n" + " },\n" + " {\n" + " \"metric_name\": \"request.total\",\n" + " \"metric_type\": \"counter\",\n" + " \"metric_description\": \"\",\n" + " \"labels\": \"{agent=\\\"Unknown\\\",assetId=\\\"asset id\\\",id=\\\"87\\\"," + "metricName=\\\"Bytes per URL\\\",method=\\\"POST\\\",url=\\\"/index.html\\\"}\",\n" + " \"value\": \"1\"\n" + " },\n" + " {\n" + " \"metric_name\": \"request.total\",\n" + " \"metric_type\": \"counter\",\n" + " \"metric_description\": \"\",\n" + " \"labels\": \"{agent=\\\"Unknown\\\",assetId=\\\"asset id\\\",id=\\\"87\\\"," + "metricName=\\\"Bytes per URL\\\",method=\\\"GET\\\",url=\\\"/index2.html\\\"}\",\n" + " \"value\": \"1\"\n" + " },\n" + " {\n" + " \"metric_name\": \"cpuMax\",\n" + " \"metric_type\": \"gauge\",\n" + " \"metric_description\": \"\",\n" + " \"labels\": \"{agent=\\\"Unknown\\\",assetId=\\\"asset id\\\",id=\\\"87\\\"," + "metricName=\\\"CPU usage\\\"}\",\n" + " \"value\": \"89\"\n" + " },\n" + " {\n" + " \"metric_name\": \"cpuMin\",\n" + " \"metric_type\": \"gauge\",\n" + " \"metric_description\": \"\",\n" + " \"labels\": \"{agent=\\\"Unknown\\\",assetId=\\\"asset id\\\",id=\\\"87\\\"," + "metricName=\\\"CPU usage\\\"}\",\n" + " \"value\": \"89\"\n" + " },\n" + " {\n" + " \"metric_name\": \"cpuAvg\",\n" + " \"metric_type\": \"gauge\",\n" + " \"metric_description\": \"\",\n" + " \"labels\": \"{agent=\\\"Unknown\\\",assetId=\\\"asset id\\\",id=\\\"87\\\"," + "metricName=\\\"CPU usage\\\"}\",\n" + " \"value\": \"89\"\n" + " },\n" + " {\n" + " \"metric_name\": \"cpuCurrent\",\n" + " \"metric_type\": \"gauge\",\n" + " \"metric_description\": \"\",\n" + " \"labels\": \"{agent=\\\"Unknown\\\",assetId=\\\"asset id\\\",id=\\\"87\\\"," + "metricName=\\\"CPU usage\\\"}\",\n" + " \"value\": \"89\"\n" + " },\n" + " {\n" + " \"metric_name\": \"cpuCounter\",\n" + " \"metric_type\": \"gauge\",\n" + " \"metric_description\": \"\",\n" + " \"labels\": \"{agent=\\\"Unknown\\\",assetId=\\\"asset id\\\",id=\\\"87\\\"," + "metricName=\\\"CPU usage\\\"}\",\n" + " \"value\": \"1\"\n" + " },\n" + " {\n" + " \"metric_name\": \"cpuTotalCounter\",\n" + " \"metric_type\": \"counter\",\n" + " \"metric_description\": \"\",\n" + " \"labels\": \"{agent=\\\"Unknown\\\",assetId=\\\"asset id\\\",id=\\\"87\\\"," + "metricName=\\\"CPU usage\\\"}\",\n" " \"value\": \"1\"\n" " }\n" " ]\n" diff --git a/nodes/orchestration/package/CMakeLists.txt b/nodes/orchestration/package/CMakeLists.txt index 6c9c4f1..f1558dd 100755 --- a/nodes/orchestration/package/CMakeLists.txt +++ b/nodes/orchestration/package/CMakeLists.txt @@ -26,6 +26,7 @@ install(FILES configuration/cp-nano-orchestration-debug-conf.json DESTINATION ./ install(FILES watchdog/watchdog DESTINATION ./orchestration/watchdog/ PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ) install(FILES watchdog/wait-for-networking-inspection-modules.sh DESTINATION ./orchestration/watchdog/ PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ) install(FILES watchdog/access_pre_init DESTINATION ./orchestration/watchdog/ PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ) +install(FILES watchdog/revert_orchestrator_version.sh DESTINATION ./orchestration/watchdog/ PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ) install(FILES local-default-policy.yaml DESTINATION ./orchestration/ PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ) install(FILES open-appsec-cloud-mgmt DESTINATION ./orchestration/ PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ) diff --git a/nodes/orchestration/package/open-appsec-cloud-mgmt-k8s b/nodes/orchestration/package/open-appsec-cloud-mgmt-k8s index a76f26b..eabf528 100755 --- a/nodes/orchestration/package/open-appsec-cloud-mgmt-k8s +++ b/nodes/orchestration/package/open-appsec-cloud-mgmt-k8s @@ -125,6 +125,20 @@ generate_policy() done done + all_policyactivations=$(curl -s --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" \ + -X GET ${APISERVER}/apis/openappsec.io/v1beta2/policyactivations) + policyactivation_list=$(echo $all_policyactivations | /etc/cp/bin/yq eval '.items[].metadata.name' -) + for policyactivation_name in ${policyactivation_list}; do + policyactivation_crd=$(curl -s --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" \ + -X GET ${APISERVER}/apis/openappsec.io/v1beta2/policyactivations/${policyactivation_name}) + policyactivation_crd=$(echo $policyactivation_crd | tr -d '\n') + if [ "$FIRST" = "0" ]; then + POLICY="$POLICY ," + fi + POLICY="$POLICY $policyactivation_crd" + FIRST="0" + done + POLICY="$POLICY ] } } }" echo $POLICY > $POLICY_CRDS_PATH } diff --git a/nodes/orchestration/package/orchestration_package.sh b/nodes/orchestration/package/orchestration_package.sh index 625199b..ca1eb85 100755 --- a/nodes/orchestration/package/orchestration_package.sh +++ b/nodes/orchestration/package/orchestration_package.sh @@ -593,8 +593,10 @@ install_watchdog() cp_exec "mkdir -p ${FILESYSTEM_PATH}/${WATCHDOG_PATH}" cp_copy watchdog/watchdog ${FILESYSTEM_PATH}/${WATCHDOG_PATH}/cp-nano-watchdog cp_copy watchdog/wait-for-networking-inspection-modules.sh ${FILESYSTEM_PATH}/${WATCHDOG_PATH}/wait-for-networking-inspection-modules.sh + cp_copy watchdog/revert_orchestrator_version.sh ${FILESYSTEM_PATH}/${WATCHDOG_PATH}/revert_orchestrator_version.sh cp_exec "chmod 700 ${FILESYSTEM_PATH}/${WATCHDOG_PATH}/cp-nano-watchdog" cp_exec "chmod 700 ${FILESYSTEM_PATH}/${WATCHDOG_PATH}/wait-for-networking-inspection-modules.sh" + cp_exec "chmod 700 ${FILESYSTEM_PATH}/${WATCHDOG_PATH}/revert_orchestrator_version.sh" cp_exec "touch ${FILESYSTEM_PATH}/${WATCHDOG_PATH}/wd.services" cp_exec "${FILESYSTEM_PATH}/${WATCHDOG_PATH}/cp-nano-watchdog --register $is_upgrade ${FILESYSTEM_PATH}/${SERVICE_PATH}/cp-nano-orchestration $var_arch_flag" diff --git a/nodes/orchestration/package/watchdog/revert_orchestrator_version.sh b/nodes/orchestration/package/watchdog/revert_orchestrator_version.sh new file mode 100755 index 0000000..c411541 --- /dev/null +++ b/nodes/orchestration/package/watchdog/revert_orchestrator_version.sh @@ -0,0 +1,79 @@ +#!/bin/sh + +SCRIPT_FOLDER=$(dirname "$0") +PARENT_FOLDER=$(dirname "$SCRIPT_FOLDER") +FILESYSTEM_PATH=$PARENT_FOLDER +UPGRADE_STATUS_FILE=${FILESYSTEM_PATH}/revert/upgrade_status +FORBIDDEN_VERSIONS_FILE=${FILESYSTEM_PATH}/revert/forbidden_versions +LAST_KNOWN_WORKING_ORCHESTRATOR=${FILESYSTEM_PATH}/revert/last_known_working_orchestrator +LOG_FILE=$1 +CONFIG_FILE="${FILESYSTEM_PATH}/conf/cp-nano-orchestration-conf.json" + +get_configuration_with_default() +{ + section="$1" + key="$2" + default_value="$3" + + local value + value=$(awk -v section="$section" -v k="$key" -v def_value="$default_value" ' + BEGIN { + found_section=0; + found_key=0; + } + $0 ~ "\"" section "\"" { found_section=1; next; } + found_section && $0 ~ "\"" k "\"" { + found_key=1; + next; + } + found_key && $0 ~ /"value"/ { + match($0, /"value"[[:space:]]*:[[:space:]]*"?([^",}]*)"?/, arr); + if (arr[1] != "") + print arr[1]; + exit; + } + found_section && $0 ~ /^\}/ { found_section=0; found_key=0; } + END { + if (!found_key) print def_value; + } + ' "$CONFIG_FILE") + + echo "$value" +} + +log() +{ + curr_date_time=$(date +%Y-%m-%dT%H:%M:%S) + callee_function=${1} + echo "[${curr_date_time}@${callee_function}] ${2}" >>${LOG_FILE} +} + +if [ -f "$UPGRADE_STATUS_FILE" ]; then + awk '{print $2}' "$UPGRADE_STATUS_FILE" >> "$FORBIDDEN_VERSIONS_FILE" + cp "$UPGRADE_STATUS_FILE" ${FILESYSTEM_PATH}/revert/failed_upgrade_info +fi + +if [ -f "$LAST_KNOWN_WORKING_ORCHESTRATOR" ]; then + manifest_file_path=$(get_configuration_with_default "orchestration" "Manifest file path" "${FILESYSTEM_PATH}/conf/manifest.json") + cp ${FILESYSTEM_PATH}/revert/last_known_manifest "$manifest_file_path" + + to_version=$(awk '{print $2}' "$UPGRADE_STATUS_FILE") + last_known_orch_version=$($LAST_KNOWN_WORKING_ORCHESTRATOR --version) + log "revert_orchestrator_version.sh" "Reverting orchestration version $to_version to last known working orchestrator (version: $last_known_orch_version)" + installation_flags="--install" + + trusted_ca_directory=$(get_configuration_with_default "message" "Trusted CA directory" "") + if [ -n "$trusted_ca_directory" ]; then + installation_flags="${installation_flags} --certs-dir ${trusted_ca_directory}" + fi + if grep -q '^CP_VS_ID=' ${FILESYSTEM_PATH}/conf/environment-details.cfg; then + cp_vs_id=$(grep '^CP_VS_ID=' "$config_file" | cut -d'=' -f2) + installation_flags="${installation_flags} --vs_id ${cp_vs_id}" + fi + + chmod +x ${LAST_KNOWN_WORKING_ORCHESTRATOR} + $LAST_KNOWN_WORKING_ORCHESTRATOR ${installation_flags} +else + log "revert_orchestrator_version.sh" "Last known working orchestrator not found" + exit 1 +fi diff --git a/nodes/orchestration/package/watchdog/watchdog b/nodes/orchestration/package/watchdog/watchdog index c8fa4c3..ababdf7 100755 --- a/nodes/orchestration/package/watchdog/watchdog +++ b/nodes/orchestration/package/watchdog/watchdog @@ -36,6 +36,8 @@ TMP_VOL_SRVS_FILE_PRE_DEL=watchdog/wd.volatile_services.del SRVS_HALTED=watchdog/wd.services.halt SERVICE_LOG_FILE_TTL_MINUTES=10080 PIDOF_CMD_EXISTS=0 +CONFIG_FILE="${FILESYSTEM_PATH}/conf/cp-nano-orchestration-conf.json" +SETTINGS_FILE="${FILESYSTEM_PATH}/conf/settings.json" env_details_file=conf/environment-details.cfg @@ -48,6 +50,41 @@ VS_EVAL_PREFIX= var_service_startup= var_upgarde=false +get_profile_agent_setting_with_default() { + key="$1" + default_value="$2" + value=$(grep -oP "\"key\":\s*\"$key\".*?\"value\":\s*\"[^\"]+\"" $SETTINGS_FILE | sed -E 's/.*"value":\s*"([^"]+)".*/\1/') + if [ "$value" = "null" ] || [ -z "$value" ]; then + echo "$default_value" + else + echo "$value" + fi +} + +MAX_ORCH_RESTARTS=$(get_profile_agent_setting_with_default "maxOrchestrationRestartsWithinThreeMin" "10") +MAX_AGE_MINUTES=$(get_profile_agent_setting_with_default "upgradeProcessTimeoutMin" "90") +MAX_AGE_SECONDS=$((MAX_AGE_MINUTES * 60)) + +update_orchestrations_counters() +{ + current_time=$(date +%s) + elapsed_time=$((current_time - last_update)) + intervals_passed=$((elapsed_time / interval_duration)) + + if [ "$intervals_passed" -gt 0 ]; then + shifts=$((intervals_passed > 3 ? 3 : intervals_passed)) + for _ in $(seq 1 "$shifts"); do + orch_counters="0 $(echo "$orch_counters" | cut -d' ' -f1-2)" + done + last_update=$((last_update + intervals_passed * interval_duration)) + fi + + first=$(echo "$orch_counters" | cut -d' ' -f1) + rest=$(echo "$orch_counters" | cut -d' ' -f2-) + first=$((first + 1)) + orch_counters="$first $rest" +} + get_basename() { is_basename="$(command -v basename)" @@ -830,6 +867,16 @@ load_services() else var_service_startup=false fi + + crashes_revert=$(get_profile_agent_setting_with_default "allowCrashesRevert" "true") + if [ "$crashes_revert" = "true" ] && [ "$(get_basename $service)" = "cp-nano-orchestration" ] && [ -f ${FILESYSTEM_PATH}/revert/upgrade_status ]; then + update_orchestrations_counters + total_orch_restarts=$(echo "$orch_counters" | awk '{print $1 + $2 + $3}') + log "load_services" "orchestrator restart no. ${total_orch_restarts}" + if [ "$total_orch_restarts" -ge "$MAX_ORCH_RESTARTS" ]; then + ${SCRIPT_FOLDER}/revert_orchestrator_version.sh ${LOG_FILE_PATH}/$LOG_FILE + fi + fi run_service $service $gaia_ld_path increment_watchdog_process_restart_counter echo "running" > $AGENT_RUN_STATUS_FILE @@ -1010,6 +1057,12 @@ else fi IS_SERVICE_STARTED=false echo "" >${FILESYSTEM_PATH}/$SRVS_HALTED + +last_update=$(date +%s) +interval_duration=60 +orch_counters="0 0 0" +iteration_count=0 + while $(true); do if [ -z $IS_CONTAINER_ENV ] && [ -f ${FILESYSTEM_PATH}/orchestration/restart_watchdog ]; then rm -f ${FILESYSTEM_PATH}/orchestration/restart_watchdog @@ -1028,5 +1081,21 @@ while $(true); do rotate_service_log daily_log_files_cleanup + + file_age_revert=$(get_profile_agent_setting_with_default "allowFileAgeRevert" "false") + iteration_count=$((iteration_count + 1)) + if [ $((iteration_count % 10)) -eq 0 ]; then + if [ "$file_age_revert" = "true" ] && [ -f ${FILESYSTEM_PATH}/revert/upgrade_status ]; then + file_mtime=$(stat -c %Y "${FILESYSTEM_PATH}/revert/upgrade_status") + current_time=$(date +%s) + file_age=$((current_time - file_mtime)) + + if [ "$file_age" -gt "$MAX_AGE_SECONDS" ]; then + log "monitor_upgrade_status_file_age" "The file has existed for more than $MAX_AGE_MINUTES minutes." + ${SCRIPT_FOLDER}/revert_orchestrator_version.sh ${LOG_FILE_PATH}/$LOG_FILE + fi + fi + fi + sleep 5 done