Feb 10 2025 dev (#255)

* sync code

* sync code

* code sync

* code sync

---------

Co-authored-by: Ned Wright <nedwright@proton.me>
Co-authored-by: Daniel Eisenberg <danielei@checkpoint.com>
This commit is contained in:
Daniel-Eisenberg 2025-02-12 10:56:44 +02:00 committed by GitHub
parent 81433bac25
commit 4ddcd2462a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
75 changed files with 1540 additions and 258 deletions

View File

@ -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()
{

View File

@ -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);

View File

@ -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

View File

@ -31,6 +31,7 @@
#include <stdarg.h>
#include <boost/range/iterator_range.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/regex.hpp>
#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<string> 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<string> ignored_headers;
// Interfaces
I_Socket *i_socket = nullptr;

View File

@ -240,6 +240,21 @@ HttpAttachmentConfig::setRetriesForVerdict()
"Max retries for verdict"
));
conf_data.setNumericalValue("hold_verdict_retries", getAttachmentConf<uint>(
3,
"agent.retriesForHoldVerdict.nginxModule",
"HTTP manager",
"Retries for hold verdict"
));
conf_data.setNumericalValue("hold_verdict_polling_time", getAttachmentConf<uint>(
1,
"agent.holdVerdictPollingInterval.nginxModule",
"HTTP manager",
"Hold verdict polling interval seconds"
));
conf_data.setNumericalValue("body_size_trigger", getAttachmentConf<uint>(
200000,
"agent.reqBodySizeTrigger.nginxModule",

View File

@ -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<NginxAttachmentOpaque>(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<string, string> 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;
}

View File

@ -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;

View File

@ -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<Buffer, CompressionType> NginxParser::content_encodings = {
{Buffer("identity"), CompressionType::NO_COMPRESSION},
@ -177,22 +178,54 @@ getActivetenantAndProfile(const string &str, const string &deli = ",")
}
Maybe<vector<HttpHeader>>
NginxParser::parseRequestHeaders(const Buffer &data)
NginxParser::parseRequestHeaders(const Buffer &data, const unordered_set<string> &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<I_TableSpecific<SessionID>>::by<NginxAttachment>();
auto parsed_headers = maybe_parsed_headers.unpack();
NginxAttachmentOpaque &opaque = i_transaction_table->getState<NginxAttachmentOpaque>();
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<string>(header.getKey());
string hdr_val = static_cast<string>(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>(
UsersAllIdentifiersConfig(),
"rulebase",
"usersIdentifiers"
);
source_identifiers.parseRequestHeaders(header);
NginxAttachmentOpaque &opaque = i_transaction_table->getState<NginxAttachmentOpaque>();
opaque.addToSavedData(
HttpTransactionData::req_headers,
static_cast<string>(header.getKey()) + ": " + static_cast<string>(header.getValue()) + "\r\n"

View File

@ -28,7 +28,10 @@ public:
static Maybe<HttpTransactionData> parseStartTrasaction(const Buffer &data);
static Maybe<ResponseCode> parseResponseCode(const Buffer &data);
static Maybe<uint64_t> parseContentLength(const Buffer &data);
static Maybe<std::vector<HttpHeader>> parseRequestHeaders(const Buffer &data);
static Maybe<std::vector<HttpHeader>> parseRequestHeaders(
const Buffer &data,
const std::unordered_set<std::string> &ignored_headers
);
static Maybe<std::vector<HttpHeader>> parseResponseHeaders(const Buffer &data);
static Maybe<HttpBody> parseRequestBody(const Buffer &data);
static Maybe<HttpBody> parseResponseBody(const Buffer &raw_response_body, CompressionStream *compression_stream);

View File

@ -15,18 +15,14 @@
#include <string>
#include <map>
#include <sys/stat.h>
#include <climits>
#include <unordered_map>
#include <unordered_set>
#include <boost/range/iterator_range.hpp>
#include <boost/algorithm/string.hpp>
#include <fstream>
#include <algorithm>
#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<I_Table>::by<HttpManager>();
Singleton::Consume<I_Logging>::by<HttpManager>()->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<string> 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<string>(event.getKey())) != ignored_headers.end()) {
dbgTrace(D_HTTP_MANAGER)
<< "Ignoring header key - "
<< static_cast<string>(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<HttpManagerOpaque>();
string event_key = static_cast<string>(event.getKey());
if (event_key == getProfileAgentSettingWithDefault<string>("", "agent.customHeaderValueLogging")) {
string event_value = static_cast<string>(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<string> ignored_headers;
};
const ngx_http_cp_verdict_e HttpManager::Impl::default_verdict(ngx_http_cp_verdict_e::TRAFFIC_VERDICT_DROP);

View File

@ -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:

View File

@ -62,6 +62,7 @@ public:
private:
Maybe<std::string> downloadPackage(const Package &package, bool is_clean_installation);
std::string getCurrentTimestamp();
std::string manifest_file_path;
std::string temp_ext;

View File

@ -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<WaapTelemetryEvent>
@ -132,6 +133,7 @@ private:
std::map<std::string, std::shared_ptr<T>>& telemetryMap
) {
if (!telemetryMap.count(asset_id)) {
dbgTrace(D_WAAP) << "creating telemetry data for asset: " << data.assetName;
telemetryMap.emplace(asset_id, std::make_shared<T>());
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<std::string>(
@ -152,29 +156,30 @@ private:
std::string("Web Application"),
EnvKeyAttr::LogSection::SOURCE
);
telemetryMap[asset_id]->template registerContext<std::string>(
"assetId",
asset_id,
EnvKeyAttr::LogSection::SOURCE
);
telemetryMap[asset_id]->template registerContext<std::string>(
"assetName",
data.assetName,
EnvKeyAttr::LogSection::SOURCE
);
telemetryMap[asset_id]->template registerContext<std::string>(
"practiceId",
data.practiceId,
EnvKeyAttr::LogSection::SOURCE
);
telemetryMap[asset_id]->template registerContext<std::string>(
"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<std::string>(
"assetId",
asset_id,
EnvKeyAttr::LogSection::SOURCE
);
telemetryMap[asset_id]->template registerContext<std::string>(
"assetName",
data.assetName,
EnvKeyAttr::LogSection::SOURCE
);
telemetryMap[asset_id]->template registerContext<std::string>(
"practiceId",
data.practiceId,
EnvKeyAttr::LogSection::SOURCE
);
telemetryMap[asset_id]->template registerContext<std::string>(
"practiceName",
data.practiceName,
EnvKeyAttr::LogSection::SOURCE
);
}
};

View File

@ -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<I_AgentDetails>,
Singleton::Consume<I_Messaging>,
Singleton::Consume<I_Encryptor>,
Singleton::Consume<I_Environment>
Singleton::Consume<I_Environment>,
Singleton::Consume<I_WaapModelResultLogger>
{
public:
WaapComponent();

View File

@ -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<InnerException> &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

View File

@ -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<InnerException> &exceptions);
void save(cereal::JSONOutputArchive &out_ar) const;

View File

@ -45,7 +45,6 @@ public:
private:
std::string name;
std::string mode;
std::vector<std::string> hosts;
};

View File

@ -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<AnnotationTypes, std::string> &rule_annotations
std::map<AnnotationTypes, std::string> &rule_annotations,
std::vector<InnerException>
);
void

View File

@ -698,8 +698,12 @@ K8sPolicyUtils::createAppsecPolicies()
}
}
auto maybe_policy_activation =
getObjectFromCluster<PolicyActivationData>("/apis/openappsec.io/v1beta2/policyactivations");
string ns_suffix = getAppSecScopeType() == "namespaced" ? "ns" : "";
string ns = getAppSecScopeType() == "namespaced" ? "namespaces/" : "";
auto maybe_policy_activation = getObjectFromCluster<PolicyActivationData>(
"/apis/openappsec.io/v1beta2/" + ns + agent_ns + "policyactivations" + ns_suffix
);
if (!maybe_policy_activation.ok()) {
dbgWarning(D_LOCAL_POLICY)

View File

@ -69,7 +69,7 @@ Identifier::load(cereal::JSONInputArchive &archive_in)
dbgWarning(D_LOCAL_POLICY) << "AppSec identifier invalid: " << identifier;
identifier = "sourceip";
}
parseMandatoryAppsecJSONKey<vector<string>>("value", value, archive_in);
parseAppsecJSONKey<vector<string>>("value", value, archive_in);
}
const string &

View File

@ -18,14 +18,6 @@ using namespace std;
USE_DEBUG_FLAG(D_LOCAL_POLICY);
static const set<string> 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<vector<string>>("hosts", hosts, archive_in);
parseAppsecJSONKey<string>("name", name, archive_in);
parseAppsecJSONKey<string>("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 &

View File

@ -928,7 +928,6 @@ createMultiRulesSections(
PracticeSection practice = PracticeSection(practice_id, practice_type, practice_name);
vector<ParametersSection> 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<AnnotationTypes, string> &rule_annotations)
map<AnnotationTypes, string> &rule_annotations,
vector<InnerException> rule_inner_exceptions)
{
auto apssec_practice =
getAppsecPracticeSpec<V1beta2AppsecLinuxPolicy, NewAppSecPracticeSpec>(
@ -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]]);
}
}

View File

@ -100,6 +100,7 @@ private:
string packages_dir;
string orch_service_name;
set<string> ignore_packages;
Maybe<string> 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<I_OrchestrationTools>::by<ManifestController>()->doesFileExist(ignore_packages_path)) {
auto orchestration_tools = Singleton::Consume<I_OrchestrationTools>::by<ManifestController>();
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<string, Package> 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<string, Package> all_packages = parsed_manifest.unpack();
map<string, Package> current_packages;
parsed_manifest = orchestration_tools->loadPackagesFromJson(manifest_file_path);

View File

@ -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<string> 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<string>(
"/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<string, Package> 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<string>(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<string> 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<string>(
"/etc/cp/conf/manifest.json",
@ -1839,6 +1973,7 @@ public:
StrictMock<MockOrchestrationStatus> mock_status;
StrictMock<MockDownloader> mock_downloader;
StrictMock<MockOrchestrationTools> mock_orchestration_tools;
StrictMock<MockDetailsResolver> mock_details_resolver;
NiceMock<MockShellCmd> 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<string> forbidden_versions(string("a1\na2"));
EXPECT_CALL(mock_orchestration_tools, readFile("/etc/cp/revert/forbidden_versions"))
.WillOnce(Return(forbidden_versions));
manifest_controller.init();
}

View File

@ -14,6 +14,7 @@
#include "manifest_handler.h"
#include <algorithm>
#include <ctime>
#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<I_OrchestrationStatus>::by<ManifestHandler>();
auto details_resolver = Singleton::Consume<I_DetailsResolver>::by<ManifestHandler>();
auto orchestration_tools = Singleton::Consume<I_OrchestrationTools>::by<ManifestHandler>();
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<I_AgentDetails>::by<ManifestHandler>();
auto hostname = Singleton::Consume<I_DetailsResolver>::by<ManifestHandler>()->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<I_OrchestrationTools>::by<ManifestHandler>();
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);
}

View File

@ -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<uint>(10, "successUpgradeInterval");
Singleton::Consume<I_MainLoop>::by<OrchestrationComp>()->addOneTimeRoutine(
I_MainLoop::RoutineType::Timer,
[this, check_upgrade_success_interval]()
{
Singleton::Consume<I_MainLoop>::by<OrchestrationComp>()->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<string>(
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<uint>(10, "successUpgradeInterval");
// LCOV_EXCL_START
Singleton::Consume<I_MainLoop>::by<OrchestrationComp>()->addOneTimeRoutine(
I_MainLoop::RoutineType::Timer,
[this, check_upgrade_success_interval]()
{
Singleton::Consume<I_MainLoop>::by<OrchestrationComp>()->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<I_DetailsResolver>::by<OrchestrationComp>();
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<void>
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<void>();
}
@ -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<int>())) {
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<I_MainLoop>::by<OrchestrationComp>();
mainloop->addOneTimeRoutine(
I_MainLoop::RoutineType::Offline,
@ -1629,7 +1760,7 @@ private:
}
}
string server_name = getAttribute("registered-server", "registered_server");
string server_name = Singleton::Consume<I_AgentDetails>::by<OrchestrationComp>()->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<vector<string>>("upgradeDay");
registerExpectedSetting<string>("email-address");
registerExpectedSetting<string>("registered-server");
registerExpectedSetting<uint>("successUpgradeInterval");
registerExpectedConfigFile("orchestration", Config::ConfigFileType::Policy);
registerExpectedConfigFile("registration-data", Config::ConfigFileType::Policy);
}

View File

@ -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;
};

View File

@ -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<chrono::microseconds>()))
.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<Config::I_Config>::from(config_comp)->loadConfiguration(ss);
EXPECT_CALL(mock_ml, yield(A<chrono::microseconds>()))
.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<string> 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<chrono::microseconds>())).WillOnce(Return());
runUpgradeRoutine();
}
TEST_F(OrchestrationTest, getBadPolicyUpdate)

View File

@ -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<I_AgentDetails>::by<FogAuthenticator>();
auto fog_address = i_agent_details->getFogDomain();

View File

@ -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<typename T>
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;

View File

@ -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 <memory>
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;

View File

@ -91,6 +91,7 @@ add_library(waap_clib
ParserScreenedJson.cc
ParserBinaryFile.cc
RegexComparator.cc
RequestsMonitor.cc
)
add_definitions("-Wno-unused-function")

View File

@ -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<I_AgentDetails>() ?
Singleton::Consume<I_AgentDetails>::by<WaapComponent>()->getOrchestrationMode() : OrchestrationMode::ONLINE;
bool enabled = getProfileAgentSettingWithDefault<bool>(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<chrono::minutes>(
Singleton::Consume<I_TimeGet>::by<WaapComponent>()->getWalltime()
).count()][source]++;
}
// LCOV_EXCL_START Reason: internal functions not used
void SourcesRequestMonitor::pullData(const vector<string> &data)
{
// not used. report only
}
void SourcesRequestMonitor::processData()
{
// not used. report only
}
void SourcesRequestMonitor::postProcessedData()
{
// not used. report only
}
void SourcesRequestMonitor::pullProcessedData(const vector<string> &data)
{
// not used. report only
}
void SourcesRequestMonitor::updateState(const vector<string> &data)
{
// not used. report only
}
// LCOV_EXCL_STOP
typedef map<string, map<string, size_t>> 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<I_AgentDetails>::by<WaapComponent>()->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);
}

View File

@ -0,0 +1,33 @@
#ifndef __REQUESTS_MONITOR_H__
#define __REQUESTS_MONITOR_H__
#include "i_serialize.h"
typedef std::map<uint64_t, std::map<std::string, size_t>> 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<std::string> &data) override;
virtual void processData() override;
virtual void postProcessedData() override;
virtual void pullProcessedData(const std::vector<std::string> &data) override;
virtual void updateState(const std::vector<std::string> &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__

View File

@ -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<I_AgentDetails>() ?
Singleton::Consume<I_AgentDetails>::by<WaapComponent>()->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 == "")
{

View File

@ -33,6 +33,7 @@ WaapTelemetryBase::sendLog(const LogRest &metric_client_rest) const
OrchestrationMode mode = Singleton::Consume<I_AgentDetails>::by<GenericMetric>()->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<I_Environment>::by<GenericMetric>()->get<bool>(
"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();
}

View File

@ -135,6 +135,7 @@ WaapAssetState::WaapAssetState(std::shared_ptr<Signatures> 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> signatures,
I_AgentDetails* agentDetails = Singleton::Consume<I_AgentDetails>::by<WaapComponent>();
std::string path = agentDetails->getTenantId() + "/" + assetId;
m_filtersMngr = std::make_shared<IndicatorsFiltersManager>(path, assetId, this);
m_requestsMonitor = std::make_shared<SourcesRequestMonitor>
(getWaapDataDir() + "/monitor.data", path, assetId, "State");
}
else
{
m_filtersMngr = std::make_shared<IndicatorsFiltersManager>("", "", this);
m_requestsMonitor = std::make_shared<SourcesRequestMonitor>
(getWaapDataDir() + "/monitor.data", "", assetId, "State");
}
// Load keyword scores - copy from ScoreBuilder
updateScores();

View File

@ -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<SourcesRequestMonitor> m_requestsMonitor;
ScoreBuilder scoreBuilder;
std::shared_ptr<Waap::RateLimiting::State> m_rateLimitingState;
std::shared_ptr<Waap::RateLimiting::State> m_errorLimitingState;
@ -90,6 +93,7 @@ public:
void logIndicatorsInFilters(const std::string &param, Waap::Keywords::KeywordsSet& keywords,
IWaf2Transaction* pTransaction);
void logParamHit(Waf2ScanResult& res, IWaf2Transaction* pTransaction);
void logSourceHit(const std::string& source);
void filterKeywords(const std::string &param, Waap::Keywords::KeywordsSet& keywords,
std::vector<std::string>& filteredKeywords);
void clearFilterVerbose();

View File

@ -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

View File

@ -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;

View File

@ -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;
}

View File

@ -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
{

View File

@ -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;
};

View File

@ -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 += "<script src=\"cp-cp.js\"></script>";
// No need to inject more than once
m_responseInjectReasons.setCaptcha(false);
}
if (m_responseInjectReasons.shouldInjectCsrf()) {
dbgTrace(D_WAAP) << "Waf2Transaction::completeInjectionResponseBody(): Injecting data (csrf)";
strInjection += "<script src=\"cp-csrf.js\"></script>";
@ -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<Waap::Trigger::Log> &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<I_Environment>::by<WaapComponent>();
auto active_id = env->get<std::string>("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<Waap::Trigger::Policy> triggerPolicy = sitePolicy.get_TriggerPolicy();
if (triggerPolicy) {
const std::shared_ptr<Waap::Trigger::Log> triggerLog = getTriggerLog(triggerPolicy);
if (triggerLog && triggerLog->webRequests) log_all = true;
}
if(decision->getThreatLevel() <= ThreatLevel::THREAT_INFO && !log_all) {
decision->setLog(false);
} else {

View File

@ -247,7 +247,9 @@ private:
const std::shared_ptr<Waap::Trigger::Log> &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;

View File

@ -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) {

View File

@ -237,6 +237,12 @@ AgentDetails::getAgentId() const
return agent_id;
}
string
AgentDetails::getRegisteredServer() const
{
return server;
}
Maybe<string>
AgentDetails::getProxy() const
{

View File

@ -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);
}

View File

@ -527,7 +527,7 @@ Debug::preload()
active_streams["FOG"] = make_shared<DebugFogStream>();
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;

View File

@ -42,6 +42,9 @@ unsigned int getFailOpenTimeout();
int isFailOpenHoldMode();
unsigned int getFailOpenHoldTimeout();
unsigned int getHoldVerdictPollingTime();
unsigned int getHoldVerdictRetries();
unsigned int getMaxSessionsPerMinute();
int isFailOpenOnSessionLimit();

View File

@ -36,6 +36,7 @@ public:
virtual Maybe<std::string> 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<std::string> 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;

View File

@ -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__

View File

@ -20,11 +20,13 @@ public:
MOCK_CONST_METHOD0(getFogDomain, Maybe<std::string>());
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<std::string>());
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());

View File

@ -73,6 +73,7 @@ public:
Maybe<std::string> 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<ProxyProtocol, ProxyData> proxies;

View File

@ -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,

View File

@ -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)

View File

@ -59,10 +59,11 @@ class GenericMetric
Singleton::Consume<I_Messaging>,
Singleton::Consume<I_RestApi>,
Singleton::Consume<I_Encryptor>,
public Listener<AllMetricEvent>
public Listener<AllMetricEvent>,
public Listener<MetricScrapeEvent>
{
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 <typename Value>
@ -96,6 +98,7 @@ public:
void resetMetrics();
void upon(const AllMetricEvent &) override;
std::string respond(const AllMetricEvent &event) override;
std::vector<PrometheusData> 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<PrometheusData> 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<MetricCalc *> calcs;
std::vector<MetricCalc *> prometheus_calcs;
Flags<Stream> active_streams;
bool reset;
bool force_buffering = false;
Context ctx;
std::string asset_id;
};
#include "metric/counter.h"

View File

@ -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 <typename Archive>
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<MetricScrapeEvent, std::vector<PrometheusData>>
{
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<PrometheusData> getPrometheusMetrics() const;
virtual std::vector<PrometheusData> getPrometheusMetrics(
const std::string &metric_name,
const std::string &asset_id = ""
) const;
virtual float getValue() const = 0;
virtual std::vector<AiopsMetricData> getAiopsMetrics() const;
@ -240,7 +268,10 @@ public:
protected:
void addMetric(GenericMetric *metric);
std::map<std::string, std::string> getBasicLabels() const;
std::map<std::string, std::string> getBasicLabels(
const std::string &metric_name,
const std::string &asset_id = ""
) const;
template <typename Metadata, typename ... OtherMetadata>
void

View File

@ -55,12 +55,17 @@ class MetricMap : public MetricCalc
}
std::vector<PrometheusData>
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<PrometheusData> 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<PrometheusData>
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<AiopsMetricData>

View File

@ -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 <string>
#include <fstream>
#include <vector>
#include <streambuf>
#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<I_RestApi>
{
public:
MetricScraper();
~MetricScraper();
void init();
private:
class Impl;
std::unique_ptr<Impl> pimpl;
};
#endif // __METRIC_SCRAPER_H__

View File

@ -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 <typename IntelligenceRest>
@ -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<uint>("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);
}

View File

@ -184,6 +184,18 @@ public:
establishConnection()
{
dbgFlow(D_CONNECTION) << "Establishing a new connection";
// check if connection already established
I_MainLoop *i_mainloop = Singleton::Consume<I_MainLoop>::by<Messaging>();
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<void>();
}
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<void, chrono::seconds>();
should_close_connection = false;
is_connected = true;
return Maybe<void>();
}
@ -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<HTTPResponse, HTTPResponse>
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<I_MainLoop>::by<Messaging>();
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<I_TimeGet>::by<Messaging>();
@ -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<string> sni_hostname = genError<string>("Uninitialized");
Maybe<string> dn_host_name = genError<string>("Uninitialized");

View File

@ -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();

View File

@ -30,7 +30,8 @@ public:
HTTPMethod method,
const std::string &uri,
const std::map<std::string, std::string> &headers,
const std::string &body
const std::string &body,
const bool should_send_access_token = true
);
Maybe<void> setConnectionHeaders(const Connection &conn, bool is_access_token_needed);

View File

@ -79,7 +79,8 @@ HTTPRequest::prepareRequest(
HTTPMethod method,
const string &uri,
const map<string, string> &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();

View File

@ -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);

View File

@ -1,3 +1,3 @@
add_library(metric generic_metric.cc)
add_library(metric generic_metric.cc metric_scraper.cc)
add_subdirectory(metric_ut)

View File

@ -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<PrometheusData>
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<string, string>
MetricCalc::getBasicLabels() const
MetricCalc::getBasicLabels(const string &metric_name, const string &asset_id) const
{
map<string, string> res;
@ -121,6 +121,9 @@ MetricCalc::getBasicLabels() const
auto executable = env->get<string>("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<PrometheusData>
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<PrometheusData>
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<Metric>()) {}
void
addMetric(const vector<PrometheusData> &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<Metric>, metrics);
};
void
GenericMetric::generatePrometheus()
{
if (!getProfileAgentSettingWithDefault(false, "prometheus")) return;
dbgTrace(D_METRICS) << "Generate prometheus metric";
vector<PrometheusData> 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<I_Messaging>::by<GenericMetric>()->sendSyncMessage(
HTTPMethod::POST,
"/add-metrics",
rest,
MessageCategory::GENERIC,
new_config_req_md
);
return all_metrics;
}
void

View File

@ -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<I_RestApi>::by<MetricScraper>()->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<PrometheusData> all_metrics;
};
MetricScraper::MetricScraper() : Component("MetricScraper"), pimpl(make_unique<MetricScraper::Impl>()) {}
MetricScraper::~MetricScraper() {}
void
MetricScraper::init()
{
pimpl->init();
}

View File

@ -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<bool>(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<string()> 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<string()> 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<string()> 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<Config::I_Config>::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"

View File

@ -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)

View File

@ -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
}

View File

@ -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"

View File

@ -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

View File

@ -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