First release of open-appsec source code

This commit is contained in:
roybarda
2022-10-26 19:33:19 +03:00
parent 3883109caf
commit a883352f79
1353 changed files with 276290 additions and 1 deletions

View File

@@ -0,0 +1,3 @@
add_library(update_communication update_communication.cc hybrid_communication.cc fog_communication.cc fog_authenticator.cc local_communication.cc)
add_subdirectory(update_communication_ut)

View File

@@ -0,0 +1,572 @@
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "fog_communication.h"
#include "rest.h"
#include "config.h"
#include "log_generator.h"
#include "agent_details.h"
#include "version.h"
#include "sasal.h"
#include <algorithm>
#include <map>
#include <vector>
SASAL_START // Orchestration - Communication
using namespace std;
using namespace cereal;
using HTTPMethod = I_Messaging::Method;
USE_DEBUG_FLAG(D_ORCHESTRATOR);
function<Maybe<FogAuthenticator::AccessToken>()> FogAuthenticator::AccessTokenProvider::getAccessToken = nullptr;
FogAuthenticator::AccessToken::AccessToken(const string &_token, chrono::seconds _expiration)
:
token(_token),
expiration(_expiration)
{
received_time = Singleton::Consume<I_TimeGet>::by<FogAuthenticator>()->getMonotonicTime();
}
chrono::seconds
FogAuthenticator::AccessToken::getRemainingTime() const
{
return
expiration -
chrono::duration_cast<chrono::seconds>(
Singleton::Consume<I_TimeGet>::by<FogAuthenticator>()->getMonotonicTime() - received_time
);
}
void
FogAuthenticator::AccessTokenProvider::doCall()
{
if (getAccessToken != nullptr) {
auto access_token = getAccessToken();
if (access_token.ok()) {
auto encryptor = Singleton::Consume<I_Encryptor>::by<FogAuthenticator>();
token = encryptor->obfuscateXorBase64(access_token.unpack().getToken());
expiration = access_token.unpack().getRemainingTime().count();
}
}
}
FogAuthenticator::RegistrationData::RegistrationData(const string &token)
:
type(AuthenticationType::Token),
data(token)
{
}
FogAuthenticator::UserCredentials::UserCredentials(const string &_client_id, const string &_shared_secret)
:
client_id(_client_id),
shared_secret(_shared_secret)
{
}
void
FogAuthenticator::UserCredentials::serialize(JSONOutputArchive &out_ar) const
{
out_ar(
make_nvp("client_id", client_id),
make_nvp("shared_secret", shared_secret)
);
}
void
FogAuthenticator::UserCredentials::serialize(JSONInputArchive &in_ar)
{
in_ar(
make_nvp("client_id", client_id),
make_nvp("shared_secret", shared_secret)
);
if (client_id.empty() || shared_secret.empty()) {
throw cereal::Exception("Agent credentials can't be empty.");
}
}
void
FogAuthenticator::RegistrationData::serialize(JSONInputArchive &in_ar)
{
string type_as_string;
static const map<string, AuthenticationType> StringToAuthenticationType {
{ "token", AuthenticationType::Token },
{ "presharedsecret", AuthenticationType::PresharedSecret }
};
in_ar(
make_nvp("registration type", type_as_string),
make_nvp("registration data", data)
);
if (type_as_string.empty()) throw cereal::Exception("registration type can't be empty.");
if (data.empty()) throw cereal::Exception("registration data can't be empty.");
auto auth_type = StringToAuthenticationType.find(type_as_string);
if (auth_type == StringToAuthenticationType.end()) throw cereal::Exception("Unsupported registration type.");
type = auth_type->second;
}
void
FogAuthenticator::RegistrationData::serialize(JSONOutputArchive &out_ar) const
{
static const EnumArray<AuthenticationType, string> AuthenticationTypeString {
"token",
"presharedsecret"
};
out_ar(
make_nvp("authenticationMethod", AuthenticationTypeString[type]),
make_nvp("data", data)
);
}
Maybe<FogAuthenticator::UserCredentials>
FogAuthenticator::registerAgent(
const FogAuthenticator::RegistrationData &reg_data,
const string &name,
const string &type,
const string &platform,
const string &architecture) const
{
dbgInfo(D_ORCHESTRATOR) << "Starting agent registration to fog";
auto details_resolver = Singleton::Consume<I_DetailsResolver>::by<FogAuthenticator>();
RegistrationRequest request(
reg_data,
name,
type,
platform,
architecture,
details_resolver->getAgentVersion()
);
request << make_pair("agent_version", details_resolver->getAgentVersion());
if (required_security_apps.size() > 0) {
request << make_pair("require", makeSeparatedStr(required_security_apps, ";"));
}
auto nginx_data = details_resolver->parseNginxMetadata();
if (nginx_data.ok()) {
string nginx_version;
string config_opt;
string cc_opt;
tie(config_opt, cc_opt, nginx_version) = nginx_data.unpack();
request << make_pair("nginxVersion", nginx_version);
request << make_pair("configureOpt", config_opt);
request << make_pair("extraCompilerOpt", cc_opt);
} else {
dbgDebug(D_ORCHESTRATOR) << nginx_data.getErr();
}
for (const pair<string, string> details : details_resolver->getResolvedDetails()) {
request << details;
}
if (details_resolver->isReverseProxy()) {
request << make_pair("reverse_proxy", "true");
}
if (details_resolver->isKernelVersion3OrHigher()) {
request << make_pair("isKernelVersion3OrHigher", "true");
}
if (details_resolver->isGwNotVsx()) {
request << make_pair("isGwNotVsx", "true");
}
if (details_resolver->isVersionEqualOrAboveR8110()) {
request << make_pair("isVersionEqualOrAboveR8110", "true");
}
#if defined(gaia) || defined(smb)
if (details_resolver->compareCheckpointVersion(8100, std::greater_equal<int>())) {
request << make_pair("isCheckpointVersionGER81", "true");
}
#endif // gaia || smb
auto fog_messaging = Singleton::Consume<I_Messaging>::by<FogAuthenticator>();
if (fog_messaging->sendObject(request, HTTPMethod::POST, fog_address_ex + "/agents")) {
dbgDebug(D_ORCHESTRATOR) << "Agent has registered successfully.";
auto i_agent_details = Singleton::Consume<I_AgentDetails>::by<FogAuthenticator>();
i_agent_details->setAgentId(request.getAgentId());
i_agent_details->setProfileId(request.getProfileId());
i_agent_details->setTenantId(request.getTenantId());
i_agent_details->writeAgentDetails();
auto orc_status = Singleton::Consume<I_OrchestrationStatus>::by<FogAuthenticator>();
orc_status->setAgentDetails(request.getAgentId(), request.getProfileId(), request.getTenantId());
return UserCredentials(request.getClientId(), request.getSharedSecret());
}
LogGen log(
"We suggest to check that your Agent Profile is defined and enforced",
ReportIS::Audience::SECURITY,
ReportIS::Severity::INFO,
ReportIS::Priority::MEDIUM,
LogField("source", "fog_communication"),
ReportIS::Tags::ORCHESTRATOR
);
return genError("Failed to register agent with the Fog");
}
Maybe<FogAuthenticator::AccessToken>
FogAuthenticator::getAccessToken(const UserCredentials &user_credentials) const
{
dbgDebug(D_ORCHESTRATOR) << "Requesting token from fog.";
static const string grant_type_string = "/oauth/token?grant_type=client_credentials";
TokenRequest request = TokenRequest();
auto fog_messaging = Singleton::Consume<I_Messaging>::by<FogAuthenticator>();
auto sending_result = fog_messaging->sendObject(
request,
HTTPMethod::POST,
fog_address_ex + grant_type_string,
buildBasicAuthHeader(user_credentials.getClientId(), user_credentials.getSharedSecret())
);
if (sending_result) {
auto data_path = getConfigurationWithDefault<string>(
filesystem_prefix + "/data/",
"encryptor",
"Data files directory"
);
auto orchestration_tools = Singleton::Consume<I_OrchestrationTools>::by<FogAuthenticator>();
if (!orchestration_tools->writeFile(request.getAccessToken(), data_path + session_token_file_name)) {
return genError("Failed to write new access token to file");
}
dbgInfo(D_ORCHESTRATOR) << "New access token was saved";
fog_messaging->loadAccessToken();
return AccessToken(request.getAccessToken(), chrono::seconds(request.getExpirationTime()));
}
return genError("Failed to get access token.");
}
Maybe<FogAuthenticator::RegistrationData>
FogAuthenticator::getRegistrationData()
{
if (!otp.empty()) {
reg_data = RegistrationData(otp);
return reg_data;
}
const char *env_otp = getenv("NANO_AGENT_TOKEN");
if (env_otp) {
dbgInfo(D_ORCHESTRATOR) << "Loading registration token from environment";
return RegistrationData(env_otp);
}
if (reg_data.ok()) {
dbgInfo(D_ORCHESTRATOR) << "Loading registration token from cache";
return reg_data;
}
auto reg_data_path = getConfigurationWithDefault<string>(
filesystem_prefix + "/conf/registration-data.json",
"orchestration",
"Registration data Path"
);
dbgDebug(D_ORCHESTRATOR) << "Loading registration data from " << reg_data_path;
auto orchestration_tools = Singleton::Consume<I_OrchestrationTools>::by<FogAuthenticator>();
auto raw_reg_data = orchestration_tools->readFile(reg_data_path);
if (!raw_reg_data.ok()) return genError(raw_reg_data.getErr());
dbgTrace(D_ORCHESTRATOR) << "Successfully loaded the registration data";
auto decoded_reg_data = orchestration_tools->base64Decode(raw_reg_data.unpack());
reg_data = orchestration_tools->jsonStringToObject<RegistrationData>(decoded_reg_data);
if (reg_data.ok()) {
dbgTrace(D_ORCHESTRATOR) << "Registration token has been converted to an object";
}
return reg_data;
}
bool
FogAuthenticator::saveCredentialsToFile(const UserCredentials &user_credentials) const
{
auto data_path = getConfigurationWithDefault<string>(
filesystem_prefix + "/data/",
"encryptor",
"Data files directory"
);
auto orchestration_tools = Singleton::Consume<I_OrchestrationTools>::by<FogAuthenticator>();
auto cred_str = orchestration_tools->objectToJson<UserCredentials>(user_credentials);
if (!cred_str.ok()) {
dbgWarning(D_ORCHESTRATOR) << "Failed to parse user credentials to JSON. Error: " << cred_str.getErr();
return false;
}
return orchestration_tools->writeFile(cred_str.unpack(), data_path + user_cred_file_name);
}
void
FogAuthenticator::initRestAPI()
{
AccessTokenProvider::getAccessToken = [this] () {
return access_token;
};
auto rest = Singleton::Consume<I_RestApi>::by<FogAuthenticator>();
rest->addRestCall<FogAuthenticator::AccessTokenProvider>(RestAction::SHOW, "access-token");
}
Maybe<FogAuthenticator::UserCredentials>
FogAuthenticator::getCredentialsFromFile() const
{
auto data_path = getConfigurationWithDefault<string>(
filesystem_prefix + "/data/",
"encryptor",
"Data files directory"
);
auto orchestration_tools = Singleton::Consume<I_OrchestrationTools>::by<FogAuthenticator>();
auto encrypted_cred = orchestration_tools->readFile(data_path + user_cred_file_name);
if (!encrypted_cred.ok()) return genError(encrypted_cred.getErr());
dbgTrace(D_ORCHESTRATOR) << "Read the user credentials from the file";
return orchestration_tools->jsonStringToObject<UserCredentials>(encrypted_cred.unpack());
}
Maybe<FogAuthenticator::UserCredentials>
FogAuthenticator::getCredentials()
{
auto maybe_credentials = getCredentialsFromFile();
if (maybe_credentials.ok()) {
return maybe_credentials;
}
auto reg_data = getRegistrationData();
if (!reg_data.ok()) {
return genError("Failed to load a valid registration token, Error: " + reg_data.getErr());
}
auto details_resolver = Singleton::Consume<I_DetailsResolver>::by<FogAuthenticator>();
Maybe<string> name = details_resolver->getHostname();
if (!name.ok()) return name.passErr();
Maybe<string> platform = details_resolver->getPlatform();
if (!platform.ok()) return platform.passErr();
Maybe<string> arch = details_resolver->getArch();
if (!arch.ok()) return arch.passErr();
string type = getConfigurationWithDefault<string>("Embedded", "orchestration", "Agent type");
maybe_credentials = registerAgent(reg_data.unpack(), *name, type, *platform, *arch);
auto orc_status = Singleton::Consume<I_OrchestrationStatus>::by<FogAuthenticator>();
orc_status->setRegistrationDetails(*name, type, *platform, *arch);
if (!maybe_credentials.ok()) return maybe_credentials;
auto credentials = maybe_credentials.unpack();
auto token_path = getConfigurationWithDefault<string>(
filesystem_prefix + "/conf/registration-data.json",
"orchestration",
"Registration data Path"
);
auto orchestration_tools = Singleton::Consume<I_OrchestrationTools>::by<FogAuthenticator>();
if (saveCredentialsToFile(credentials)) {
if (!orchestration_tools->removeFile(token_path)) {
dbgWarning(D_ORCHESTRATOR) << "Failed to remove one time token file";
}
return credentials;
}
dbgWarning(D_ORCHESTRATOR) << "Failed to save credentials to file";
Singleton::Consume<I_MainLoop>::by<FogAuthenticator>()->addOneTimeRoutine(
I_MainLoop::RoutineType::Offline,
[this, credentials, token_path] ()
{
auto orchestration_tools = Singleton::Consume<I_OrchestrationTools>::by<FogAuthenticator>();
static uint retry_counter = 1;
while (!saveCredentialsToFile(credentials)) {
dbgTrace(D_ORCHESTRATOR) << "Failed to save credentials to file, retry number: " << retry_counter++;
Singleton::Consume<I_MainLoop>::by<FogAuthenticator>()->yield(chrono::seconds(60));
}
if (!orchestration_tools->removeFile(token_path)) {
dbgWarning(D_ORCHESTRATOR) << "Failed to remove one time token file";
}
},
"Fog credential save to file"
);
return credentials;
}
string
FogAuthenticator::buildBasicAuthHeader(const string &username, const string &pass) const
{
auto orchestration_tools = Singleton::Consume<I_OrchestrationTools>::by<FogAuthenticator>();
auto auth_encode = orchestration_tools->base64Encode(username + ":" + pass);
return "Authorization: Basic " + auth_encode + "\r\n";
}
string
FogAuthenticator::buildOAuth2Header(const string &token) const
{
return "Authorization: Bearer " + token + "\r\n";
}
void
FogAuthenticator::setAddressExtenesion(const std::string &extension)
{
fog_address_ex = extension;
}
Maybe<void>
FogAuthenticator::authenticateAgent()
{
const int min_expiration_time = 10;
if (!credentials.ok()) {
dbgDebug(D_ORCHESTRATOR) << "Getting Agent credentials.";
auto orc_status = Singleton::Consume<I_OrchestrationStatus>::by<FogAuthenticator>();
credentials = getCredentials();
if (!credentials.ok()) {
orc_status->setFieldStatus(
OrchestrationStatusFieldType::REGISTRATION,
OrchestrationStatusResult::FAILED,
credentials.getErr()
);
return genError(credentials.getErr());
}
orc_status->setFieldStatus(
OrchestrationStatusFieldType::REGISTRATION,
OrchestrationStatusResult::SUCCESS
);
}
auto mainloop = Singleton::Consume<I_MainLoop>::by<FogAuthenticator>();
if (!mainloop->doesRoutineExist(routine)) {
routine = mainloop->addOneTimeRoutine(
I_MainLoop::RoutineType::RealTime,
[this, min_expiration_time] ()
{
uint expiration_time;
uint pre_expire_time = 0;
do {
expiration_time = 20;
auto orc_status = Singleton::Consume<I_OrchestrationStatus>::by<FogAuthenticator>();
access_token = getAccessToken(credentials.unpack());
if (access_token.ok()) {
pre_expire_time = getConfigurationWithDefault<int>(
120,
"fog communication",
"Time (seconds) to renew token prior its expiration"
);
expiration_time = access_token.unpack().getExpiration();
dbgInfo(D_ORCHESTRATOR) << "New token was received, expiration time: " << expiration_time;
orc_status->setFieldStatus(
OrchestrationStatusFieldType::REGISTRATION,
OrchestrationStatusResult::SUCCESS
);
} else {
dbgWarning(D_ORCHESTRATOR)
<< "Failed to receive access token. Error: " << access_token.getErr();
orc_status->setFieldStatus(
OrchestrationStatusFieldType::REGISTRATION,
OrchestrationStatusResult::FAILED,
access_token.getErr()
);
}
int next_session_req = max(
static_cast<int>(expiration_time - pre_expire_time),
min_expiration_time
);
dbgDebug(D_ORCHESTRATOR)
<< "Schedule the next re-activate session token. Seconds: "
<< next_session_req;
Singleton::Consume<I_MainLoop>::by<FogAuthenticator>()->yield(chrono::seconds(next_session_req));
} while (1);
},
"Fog communication token periodic update",
true
);
// Wait for the access token mainloop
mainloop->yield(chrono::seconds(min_expiration_time + 1));
}
if (!access_token.ok()) return genError(access_token.getErr());
return Maybe<void>();
}
void
FogAuthenticator::preload()
{
registerExpectedConfiguration<string>("orchestration", "Agent type");
registerExpectedConfiguration<string>("orchestration", "OTP Token Path");
registerExpectedConfiguration<string>("orchestration", "User Credentials Path");
registerExpectedConfiguration<int>("fog communication", "Time (seconds) to renew token prior its expiration");
}
void
FogAuthenticator::loadRequiredSecurityApps()
{
auto required_apps_file_path = getConfigurationWithDefault<string>(
filesystem_prefix + "/conf/support-practices.txt",
"orchestration",
"Supported practices file path"
);
auto orchestration_tools = Singleton::Consume<I_OrchestrationTools>::by<FogAuthenticator>();
if (orchestration_tools->doesFileExist(required_apps_file_path)) {
try {
ifstream input_stream(required_apps_file_path);
if (!input_stream) {
dbgDebug(D_ORCHESTRATOR)
<< "Cannot open the file with required security apps"
<< "File: " << required_apps_file_path;
return;
}
string required_security_app;
while (getline(input_stream, required_security_app)) {
required_security_apps.push_back(required_security_app);
}
input_stream.close();
} catch (const ifstream::failure &exception) {
dbgWarning(D_ORCHESTRATOR)
<< "Cannot read the file with required security app lists."
<< " File: " << required_apps_file_path
<< " Error: " << exception.what();
}
}
}
void
FogAuthenticator::init()
{
filesystem_prefix = getFilesystemPathConfig();
dbgTrace(D_ORCHESTRATOR) << "Initializing Fog communication, file system path prefix: " << filesystem_prefix;
loadRequiredSecurityApps();
initRestAPI();
}
SASAL_END

View File

@@ -0,0 +1,89 @@
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "fog_communication.h"
#include "rest.h"
#include "config.h"
#include "log_generator.h"
#include "agent_details.h"
#include "version.h"
#include "sasal.h"
#include <algorithm>
#include <map>
#include <vector>
SASAL_START // Orchestration - Communication
using namespace std;
using namespace cereal;
using HTTPMethod = I_Messaging::Method;
USE_DEBUG_FLAG(D_ORCHESTRATOR);
Maybe<void>
FogCommunication::getUpdate(CheckUpdateRequest &request)
{
if (!access_token.ok()) return genError("Acccess Token not available.");
auto unpacked_access_token = access_token.unpack().getToken();
static const string check_update_str = "/api/v2/agents/resources";
auto request_status = Singleton::Consume<I_Messaging>::by<FogCommunication>()->sendObject(
request,
HTTPMethod::POST,
fog_address_ex + check_update_str,
buildOAuth2Header(unpacked_access_token)
);
if (!request_status) {
dbgDebug(D_ORCHESTRATOR) << "Failed to get response after check update request.";
return genError("Failed to request updates");
}
dbgDebug(D_ORCHESTRATOR) << "Got response after check update request.";
return Maybe<void>();
}
Maybe<string>
FogCommunication::downloadAttributeFile(const GetResourceFile &resourse_file)
{
if (!access_token.ok()) return genError("Acccess Token not available.");
auto unpacked_access_token = access_token.unpack().getToken();
static const string file_attribute_str = "/api/v2/agents/resources/";
Maybe<string> attribute_file = Singleton::Consume<I_Messaging>::by<FogCommunication>()->downloadFile(
resourse_file,
resourse_file.getRequestMethod(),
fog_address_ex + file_attribute_str + resourse_file.getFileName(),
buildOAuth2Header(unpacked_access_token) // Header
);
return attribute_file;
}
Maybe<void>
FogCommunication::sendPolicyVersion(const string &policy_version) const
{
PolicyVersionPatchRequest request(policy_version);
auto fog_messaging = Singleton::Consume<I_Messaging>::by<FogCommunication>();
if (fog_messaging->sendNoReplyObject(request, HTTPMethod::PATCH, fog_address_ex + "/agents")) {
dbgInfo(D_ORCHESTRATOR)
<< "Patch request was sent successfully to the fog."
<< " Policy version: "
<< policy_version;
return Maybe<void>();
}
return genError("Failed to patch policy version");
}
SASAL_END

View File

@@ -0,0 +1,128 @@
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "hybrid_communication.h"
#include "rest.h"
#include "config.h"
#include "log_generator.h"
#include "agent_details.h"
#include "version.h"
#include "sasal.h"
#include <algorithm>
#include <map>
#include <vector>
SASAL_START // Orchestration - Communication
using namespace std;
using HTTPMethod = I_Messaging::Method;
USE_DEBUG_FLAG(D_ORCHESTRATOR);
void
HybridCommunication::init()
{
FogAuthenticator::init();
dbgTrace(D_ORCHESTRATOR) << "Initializing the Hybrid Communication Component";
if (getConfigurationFlag("otp") != "") {
otp = getConfigurationFlag("otp");
} else {
otp = "cp-3fb5c718-5e39-47e6-8d5e-99b4bc5660b74b4b7fc8-5312-451d-a763-aaf7872703c0";
}
}
string
HybridCommunication::getChecksum(const string &policy_version)
{
dbgFlow(D_ORCHESTRATOR) << "Checking the policy Checksum";
string clean_plicy_version = policy_version;
if (!clean_plicy_version.empty() && clean_plicy_version[clean_plicy_version.size() - 1] == '\n')
clean_plicy_version.erase(clean_plicy_version.size() - 1);
curr_policy = Singleton::Consume<I_K8S_Policy_Gen>::by<HybridCommunication>()->parsePolicy(clean_plicy_version);
I_OrchestrationTools *orchestration_tools = Singleton::Consume<I_OrchestrationTools>::by<FogAuthenticator>();
Maybe<string> file_checksum = orchestration_tools->calculateChecksum(
I_OrchestrationTools::SELECTED_CHECKSUM_TYPE,
Singleton::Consume<I_K8S_Policy_Gen>::by<HybridCommunication>()->getPolicyPath()
);
if (!file_checksum.ok()) {
dbgWarning(D_ORCHESTRATOR) << "Failed the policy checksum calculation";
return "";
}
return file_checksum.unpack();
}
Maybe<string>
HybridCommunication::getNewVersion()
{
I_OrchestrationTools *orchestration_tools = Singleton::Consume<I_OrchestrationTools>::by<FogAuthenticator>();
return orchestration_tools->readFile("/etc/cp/conf/k8s-policy-check.trigger");
}
Maybe<void>
HybridCommunication::getUpdate(CheckUpdateRequest &request)
{
dbgFlow(D_ORCHESTRATOR) << "Getting policy update in an Hybrid Communication";
auto maybe_new_version = getNewVersion();
if (!maybe_new_version.ok() || maybe_new_version == curr_version) {
request = CheckUpdateRequest("", "", "", "", "", "");
dbgDebug(D_ORCHESTRATOR) << "No new version is currently available";
return Maybe<void>();
}
auto policy_checksum = request.getPolicy();
auto offline_policy_checksum = getChecksum(maybe_new_version.unpack());
string policy_response = "";
if (!policy_checksum.ok() || offline_policy_checksum != policy_checksum.unpack()) {
policy_response = offline_policy_checksum;
}
dbgDebug(D_ORCHESTRATOR)
<< "Local update response: "
<< " policy: "
<< (policy_response.empty() ? "has no change," : "has new update," );
request = CheckUpdateRequest("", policy_response, "", "", "", "");
curr_version = *maybe_new_version;
return Maybe<void>();
}
Maybe<string>
HybridCommunication::downloadAttributeFile(const GetResourceFile &resourse_file)
{
auto file_name = resourse_file.getFileName();
if (file_name.compare("policy") == 0) {
return curr_policy;
}
dbgWarning(D_ORCHESTRATOR) << "Failed downloading the attribute files";
return string("");
}
Maybe<void>
HybridCommunication::sendPolicyVersion(const string &policy_version) const
{
dbgFlow(D_ORCHESTRATOR);
policy_version.empty();
return Maybe<void>();
}
SASAL_END

View File

@@ -0,0 +1,187 @@
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "local_communication.h"
#include "config.h"
#include "sasal.h"
SASAL_START // Orchestration - Communication
using namespace std;
USE_DEBUG_FLAG(D_ORCHESTRATOR);
void
LocalCommunication::init()
{
filesystem_prefix = getFilesystemPathConfig();
dbgTrace(D_ORCHESTRATOR) << "Initializing Local communication, file system path prefix: " << filesystem_prefix;
}
void
LocalCommunication::preload()
{
registerExpectedConfiguration<string>("orchestration", "Offline manifest file path");
registerExpectedConfiguration<string>("orchestration", "Offline settings file path");
registerExpectedConfiguration<string>("orchestration", "Offline policy file path");
registerExpectedConfiguration<string>("orchestration", "Offline Data file path");
}
Maybe<void>
LocalCommunication::authenticateAgent()
{
return Maybe<void>();
}
string
LocalCommunication::getChecksum(const string &file_path)
{
I_OrchestrationTools *orchestration_tools = Singleton::Consume<I_OrchestrationTools>::by<LocalCommunication>();
Maybe<string> file_checksum = orchestration_tools->calculateChecksum(
I_OrchestrationTools::SELECTED_CHECKSUM_TYPE,
file_path
);
if (!file_checksum.ok()) return "";
return file_checksum.unpack();
}
Maybe<void>
LocalCommunication::getUpdate(CheckUpdateRequest &request)
{
auto manifest_checksum = request.getManifest();
auto policy_checksum = request.getPolicy();
auto settings_checksum =request.getSettings();
auto data_checksum = request.getData();
auto offline_manifest_checksum = getChecksum(
getConfigurationWithDefault<string>(
filesystem_prefix + "/conf/offline_manifest.json",
"orchestration",
"Offline Manifest file path"
)
);
auto offline_policy_checksum = getChecksum(
getConfigurationWithDefault<string>(
filesystem_prefix + "/conf/offline_policy.json",
"orchestration",
"Offline Policy file path"
)
);
auto offline_settings_checksum = getChecksum(
getConfigurationWithDefault<string>(
filesystem_prefix + "/conf/offline_settings.json",
"orchestration",
"Offline Settings file path"
)
);
auto offline_data_checksum = getChecksum(
getConfigurationWithDefault<string>(
filesystem_prefix + "/conf/data/offline_data.json",
"orchestration",
"Offline Data file path"
)
);
string manifest_response = "";
string policy_response = "";
string settings_response = "";
string data_response = "";
if (!manifest_checksum.ok() || offline_manifest_checksum != manifest_checksum.unpack()) {
manifest_response = offline_manifest_checksum;
}
if (!policy_checksum.ok() || offline_policy_checksum != policy_checksum.unpack()) {
policy_response = offline_policy_checksum;
}
if (!settings_checksum.ok() || offline_settings_checksum != settings_checksum.unpack()) {
settings_response = offline_settings_checksum;
}
if (!data_checksum.ok() || offline_data_checksum != data_checksum.unpack()) {
data_response = offline_data_checksum;
}
dbgDebug(D_ORCHESTRATOR) << "Local update response, "
<< " manifest: " << (manifest_response.empty() ? "has no change," : "has new update,")
<< " policy: " << (policy_response.empty() ? "has no change," : "has new update," )
<< " settings: " << (settings_response.empty() ? "has no change" : "has new update")
<< " data: " << (data_response.empty() ? "has no change" : "has new update");
request = CheckUpdateRequest(manifest_response, policy_response, settings_response, data_response, "", "");
return Maybe<void>();
}
Maybe<string>
LocalCommunication::downloadAttributeFile(const GetResourceFile &resourse_file)
{
auto file_name = resourse_file.getFileName();
I_OrchestrationTools *orchestration_tools = Singleton::Consume<I_OrchestrationTools>::by<LocalCommunication>();
if (file_name.compare("policy") == 0) {
return orchestration_tools->readFile(getConfigurationWithDefault<string>(
filesystem_prefix + "/conf/offline_policy.json",
"orchestration",
"Offline Policy file path"
));
}
if (file_name.compare("manifest") == 0) {
return orchestration_tools->readFile(getConfigurationWithDefault<string>(
filesystem_prefix + "/conf/offline_manifest.json",
"orchestration",
"Offline Manifest file path"
));
}
if (file_name.compare("settings") == 0) {
return orchestration_tools->readFile(getConfigurationWithDefault<string>(
filesystem_prefix + "/conf/offline_settings.json",
"orchestration",
"Offline Settings file path"
));
}
if (file_name.compare("virtualSettings") == 0) {
return orchestration_tools->readFile(getConfigurationWithDefault<string>(
filesystem_prefix + "/conf/offline_virtual_manifest.json",
"orchestration",
"Offline virtual Manifest file path"
));
}
if (file_name.compare("virtualPolicy") == 0) {
return orchestration_tools->readFile(getConfigurationWithDefault<string>(
filesystem_prefix + "/conf/offline_virtual_settings.json",
"orchestration",
"Offline virtual Settings file path"
));
}
dbgError(D_ORCHESTRATOR) << "Unknown resourse file name " << file_name;
return genError("Failed to detect resourse file name " + file_name);
}
void
LocalCommunication::setAddressExtenesion(const string &)
{
dbgTrace(D_ORCHESTRATOR) << "Agent in offline mode, no need for address setting";
return;
}
Maybe<void>
LocalCommunication::sendPolicyVersion(const string &) const
{
dbgTrace(D_ORCHESTRATOR) << "Agent in offline mode, no need to send policy version";
return Maybe<void>();
}
SASAL_END

View File

@@ -0,0 +1,148 @@
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "update_communication.h"
#include <algorithm>
#include <map>
#include <vector>
#include "rest.h"
#include "config.h"
#include "log_generator.h"
#include "agent_details.h"
#include "version.h"
#include "sasal.h"
#include "i_encryptor.h"
#include "fog_authenticator.h"
#include "fog_communication.h"
#include "local_communication.h"
#include "hybrid_communication.h"
SASAL_START // Orchestration - Communication
using namespace std;
USE_DEBUG_FLAG(D_ORCHESTRATOR);
class UpdateCommunication::Impl
:
public ServerRest,
Singleton::Provide<I_UpdateCommunication>::From<UpdateCommunication>
{
public:
void
doCall() override
{
Singleton::Consume<I_MainLoop>::by<UpdateCommunication>()->stopAll();
status = "Operation mode had changed successfully";
}
void
preload()
{
FogAuthenticator::preload();
LocalCommunication::preload();
}
void
init()
{
auto rest = Singleton::Consume<I_RestApi>::by<UpdateCommunication>();
rest->addRestCall<UpdateCommunication::Impl>(RestAction::SET, "orchestration-mode");
setMode();
}
Maybe<void>
authenticateAgent()
{
return i_update_comm_impl->authenticateAgent();
}
Maybe<void>
getUpdate(CheckUpdateRequest &request) override
{
return i_update_comm_impl->getUpdate(request);
}
Maybe<void>
sendPolicyVersion(const string &policy_version) const override
{
return i_update_comm_impl->sendPolicyVersion(policy_version);
}
Maybe<string>
downloadAttributeFile(const GetResourceFile &resourse_file) override
{
return i_update_comm_impl->downloadAttributeFile(resourse_file);
}
void
setAddressExtenesion(const string &extension) override
{
i_update_comm_impl->setAddressExtenesion(extension);
}
void
fini()
{
i_update_comm_impl = nullptr;
}
private:
void
setMode()
{
if (getConfigurationFlag("orchestration-mode") == "offline_mode") {
i_update_comm_impl = make_unique<LocalCommunication>();
LocalCommunication *local_comm = static_cast<LocalCommunication*>(i_update_comm_impl.get());
local_comm->init();
return;
} else if (getConfigurationFlag("orchestration-mode") == "hybrid_mode") {
i_update_comm_impl = make_unique<HybridCommunication>();
HybridCommunication *local_comm = static_cast<HybridCommunication*>(i_update_comm_impl.get());
local_comm->init();
return;
}
i_update_comm_impl = make_unique<FogCommunication>();
FogCommunication *fog_comm = static_cast<FogCommunication*>(i_update_comm_impl.get());
fog_comm->init();
}
std::unique_ptr<I_UpdateCommunication> i_update_comm_impl = nullptr;
S2C_LABEL_PARAM(string, status, "status");
};
UpdateCommunication::UpdateCommunication() : Component("UpdateCommunication"), pimpl(make_unique<Impl>()) {}
UpdateCommunication::~UpdateCommunication() {}
void
UpdateCommunication::preload()
{
pimpl->preload();
}
void
UpdateCommunication::init()
{
pimpl->init();
}
void
UpdateCommunication::fini()
{
pimpl->fini();
}
SASAL_END

View File

@@ -0,0 +1,7 @@
link_directories(${BOOST_ROOT}/lib)
add_unit_test(
update_communication_ut
"local_communication_ut.cc"
"rest;version;orchestration_modules;update_communication;singleton;config;metric;event_is;logging;agent_details;-lboost_regex;"
)

View File

@@ -0,0 +1,233 @@
#include <string>
#include "local_communication.h"
#include "cptest.h"
#include "mock/mock_orchestration_tools.h"
#include "config.h"
#include "config_component.h"
#include "orchestration_status.h"
#include "mock/mock_mainloop.h"
#include "mock/mock_time_get.h"
using namespace std;
using namespace testing;
ostream &
operator<<(ostream &os, const tuple<OrchManifest, OrchPolicy, OrchSettings> &)
{
return os;
}
class LocalCommunicationTest: public Test
{
public:
LocalCommunicationTest()
{
local_communication.init();
}
void
preload()
{
local_communication.preload();
}
Maybe<void>
authenticateAgent()
{
return local_communication.authenticateAgent();
}
Maybe<void>
sendPolicyVersion(const string &version)
{
return local_communication.sendPolicyVersion(version);
}
Maybe<string>
downloadAttributeFile(const GetResourceFile &resourse_file)
{
return local_communication.downloadAttributeFile(resourse_file);
}
void
setAddressExtenesion(const string &ext)
{
local_communication.setAddressExtenesion(ext);
}
Maybe<void>
checkUpdate(CheckUpdateRequest &request)
{
return local_communication.getUpdate(request);
}
NiceMock<MockMainLoop> mock_mainloop;
NiceMock<MockTimeGet> mock_timer;
::Environment env;
ConfigComponent config_comp;
StrictMock<MockOrchestrationTools> mock_orc_tools;
OrchestrationStatus orc_status;
private:
LocalCommunication local_communication;
};
TEST_F(LocalCommunicationTest, doNothing)
{
}
TEST_F(LocalCommunicationTest, registerConfig)
{
env.preload();
env.init();
preload();
string config_json =
"{\n"
" \"orchestration\": {\n"
" \"Offline manifest file path\": [\n"
" {\n"
" \"context\": \"All()\",\n"
" \"value\": \"ABC\"\n"
" }\n"
" ],\n"
" \"Offline policy file path\": [\n"
" {\n"
" \"context\": \"All()\",\n"
" \"value\": \"qwe\"\n"
" }\n"
" ],\n"
" \"Offline settings file path\": [\n"
" {\n"
" \"context\": \"All()\",\n"
" \"value\": \"CCCC\"\n"
" }\n"
" ]\n"
" }\n"
"}";
istringstream ss(config_json);
Singleton::Consume<Config::I_Config>::from(config_comp)->loadConfiguration(ss);
EXPECT_THAT(getConfiguration<string>("orchestration", "Offline manifest file path"), IsValue("ABC"));
EXPECT_THAT(getConfiguration<string>("orchestration", "Offline policy file path"), IsValue("qwe"));
EXPECT_THAT(getConfiguration<string>("orchestration", "Offline settings file path"), IsValue("CCCC"));
env.fini();
}
TEST_F(LocalCommunicationTest, authenticateAgent)
{
auto authenticat_res = authenticateAgent();
EXPECT_TRUE(authenticat_res.ok());
}
TEST_F(LocalCommunicationTest, downloadManifest)
{
string new_manifest_string = "new manifest";
EXPECT_CALL(mock_orc_tools, readFile("/etc/cp/conf/offline_manifest.json")).WillOnce(Return(new_manifest_string));
GetResourceFile resourse_file(GetResourceFile::ResourceFileType::MANIFEST);
auto downloaded_string = downloadAttributeFile(resourse_file);
EXPECT_TRUE(downloaded_string.ok());
EXPECT_EQ(downloaded_string.unpack(), new_manifest_string);
}
TEST_F(LocalCommunicationTest, checkUpdateWithNoUpdate)
{
Maybe<string> manifest_checksum(string("1"));
Maybe<string> policy_checksum(string("2"));
Maybe<string> settings_checksum(string("3"));
Maybe<string> data_checksum(string("4"));
EXPECT_CALL(mock_orc_tools, calculateChecksum(
Package::ChecksumTypes::SHA256, "/etc/cp/conf/offline_manifest.json")).WillOnce(Return(manifest_checksum));
EXPECT_CALL(mock_orc_tools, calculateChecksum(
Package::ChecksumTypes::SHA256, "/etc/cp/conf/offline_policy.json")).WillOnce(Return(policy_checksum));
EXPECT_CALL(mock_orc_tools, calculateChecksum(
Package::ChecksumTypes::SHA256, "/etc/cp/conf/offline_settings.json")).WillOnce(Return(settings_checksum));
EXPECT_CALL(mock_orc_tools, calculateChecksum(
Package::ChecksumTypes::SHA256, "/etc/cp/conf/data/offline_data.json")).WillOnce(Return(data_checksum));
CheckUpdateRequest request(
*manifest_checksum,
*policy_checksum,
*settings_checksum,
*data_checksum,
I_OrchestrationTools::SELECTED_CHECKSUM_TYPE_STR,
"123"
);
auto update_response = checkUpdate(request);
EXPECT_TRUE(update_response.ok());
Maybe<string> manifest = request.getManifest();
EXPECT_FALSE(manifest.ok());
Maybe<string> policy = request.getPolicy();
EXPECT_FALSE(policy.ok());
Maybe<string> settings = request.getSettings();
EXPECT_FALSE(settings.ok());
Maybe<string> data = request.getData();
EXPECT_FALSE(data.ok());
}
TEST_F(LocalCommunicationTest, checkUpdateWithPolicyUpdate)
{
Maybe<string> manifest_checksum(string("1"));
Maybe<string> policy_checksum(string("2"));
Maybe<string> new_policy_checksum(string("22"));
Maybe<string> settings_checksum(string("3"));
Maybe<string> data_checksum(string("4"));
EXPECT_CALL(
mock_orc_tools,
calculateChecksum(Package::ChecksumTypes::SHA256, "/etc/cp/conf/offline_manifest.json")
).WillOnce(Return(manifest_checksum));
EXPECT_CALL(
mock_orc_tools,
calculateChecksum(Package::ChecksumTypes::SHA256, "/etc/cp/conf/offline_policy.json")
).WillOnce(Return(new_policy_checksum));
EXPECT_CALL(
mock_orc_tools,
calculateChecksum(Package::ChecksumTypes::SHA256, "/etc/cp/conf/offline_settings.json")
).WillOnce(Return(settings_checksum));
EXPECT_CALL(
mock_orc_tools,
calculateChecksum(Package::ChecksumTypes::SHA256, "/etc/cp/conf/data/offline_data.json")
).WillOnce(Return(data_checksum));
CheckUpdateRequest request(
*manifest_checksum,
*policy_checksum,
*settings_checksum,
*data_checksum,
I_OrchestrationTools::SELECTED_CHECKSUM_TYPE_STR,
"123"
);
auto update_response = checkUpdate(request);
EXPECT_TRUE(update_response.ok());
Maybe<string> manifest = request.getManifest();
EXPECT_FALSE(manifest.ok());
EXPECT_THAT(request.getPolicy(), IsValue("22"));
Maybe<string> settings = request.getSettings();
EXPECT_FALSE(settings.ok());
Maybe<string> data = request.getData();
EXPECT_FALSE(data.ok());
}
TEST_F(LocalCommunicationTest, setAddressExtenesion)
{
setAddressExtenesion("Test");
}
TEST_F(LocalCommunicationTest, sendPolicyVersion)
{
auto res = sendPolicyVersion("12");
EXPECT_TRUE(res.ok());
}