mirror of
https://github.com/openappsec/openappsec.git
synced 2025-12-31 13:49:08 +03:00
First release of open-appsec source code
This commit is contained in:
2
components/security_apps/CMakeLists.txt
Normal file
2
components/security_apps/CMakeLists.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
add_subdirectory(orchestration)
|
||||
add_subdirectory(waap)
|
||||
17
components/security_apps/orchestration/CMakeLists.txt
Executable file
17
components/security_apps/orchestration/CMakeLists.txt
Executable file
@@ -0,0 +1,17 @@
|
||||
add_definitions(-DUSERSPACE)
|
||||
|
||||
include_directories(include)
|
||||
|
||||
add_library(orchestration orchestration_comp.cc hybrid_mode_telemetry.cc)
|
||||
add_subdirectory(orchestration_tools)
|
||||
add_subdirectory(modules)
|
||||
add_subdirectory(downloader)
|
||||
add_subdirectory(service_controller)
|
||||
add_subdirectory(package_handler)
|
||||
add_subdirectory(manifest_controller)
|
||||
add_subdirectory(update_communication)
|
||||
add_subdirectory(details_resolver)
|
||||
add_subdirectory(health_check)
|
||||
add_subdirectory(k8s_policy_gen)
|
||||
|
||||
add_subdirectory(orchestration_ut)
|
||||
@@ -0,0 +1,3 @@
|
||||
include_directories(details_resolver_handlers)
|
||||
|
||||
add_library(details_resolver details_resolver.cc details_resolving_handler.cc)
|
||||
@@ -0,0 +1,288 @@
|
||||
// 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 "details_resolver.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "details_resolving_handler.h"
|
||||
#include "i_orchestration_tools.h"
|
||||
#include "maybe_res.h"
|
||||
#include "version.h"
|
||||
#include "config.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
USE_DEBUG_FLAG(D_ORCHESTRATOR);
|
||||
|
||||
class DetailsResolver::Impl
|
||||
:
|
||||
Singleton::Provide<I_DetailsResolver>::From<DetailsResolver>
|
||||
{
|
||||
public:
|
||||
Maybe<string> getHostname() override;
|
||||
Maybe<string> getPlatform() override;
|
||||
Maybe<string> getArch() override;
|
||||
|
||||
map<string, string> getResolvedDetails() override;
|
||||
|
||||
string getAgentVersion() override;
|
||||
bool isKernelVersion3OrHigher() override;
|
||||
bool isGwNotVsx() override;
|
||||
bool isVersionEqualOrAboveR8110() override;
|
||||
bool isReverseProxy() override;
|
||||
Maybe<tuple<string, string, string>> parseNginxMetadata() override;
|
||||
#if defined(gaia) || defined(smb)
|
||||
bool compareCheckpointVersion(int cp_version, std::function<bool(int, int)> compare_operator) const override;
|
||||
#endif // gaia || smb
|
||||
|
||||
private:
|
||||
#if defined(gaia) || defined(smb)
|
||||
int getCheckpointVersion() const;
|
||||
#endif // gaia || smb
|
||||
|
||||
DetailsResolvingHanlder handler;
|
||||
};
|
||||
|
||||
map<string, string>
|
||||
DetailsResolver::Impl::getResolvedDetails()
|
||||
{
|
||||
return handler.getResolvedDetails();
|
||||
}
|
||||
|
||||
Maybe<string>
|
||||
DetailsResolver::Impl::getHostname()
|
||||
{
|
||||
#if defined(arm32_musl) || defined(openwrt)
|
||||
auto host_name = DetailsResolvingHanlder::getCommandOutput("uname -a | awk '{print $(2)}'");
|
||||
#else // not arm32_musl || openwrt
|
||||
auto host_name = DetailsResolvingHanlder::getCommandOutput("hostname");
|
||||
#endif // defined(arm32_musl) || defined(openwrt)
|
||||
if (!host_name.ok()) return genError("Failed to load host name, Error: " + host_name.getErr());
|
||||
return host_name;
|
||||
}
|
||||
|
||||
Maybe<string>
|
||||
DetailsResolver::Impl::getPlatform()
|
||||
{
|
||||
#if defined(gaia)
|
||||
return string("gaia");
|
||||
#elif defined(arm32_rpi)
|
||||
return string("glibc");
|
||||
#elif defined(arm32_musl)
|
||||
return string("musl");
|
||||
#elif defined(smb_mrv_v1)
|
||||
return string("smb_mrv_v1");
|
||||
#elif defined(smb_sve_v2)
|
||||
return string("smb_sve_v2");
|
||||
#elif defined(smb_thx_v3)
|
||||
return string("smb_thx_v3");
|
||||
#elif defined(openwrt)
|
||||
return string("uclibc");
|
||||
#elif defined(arm64_linaro)
|
||||
return string("arm64_linaro");
|
||||
#elif defined(alpine)
|
||||
return string("alpine");
|
||||
#elif defined(arm64_trustbox)
|
||||
return string("arm64_trustbox");
|
||||
#elif defined(linux)
|
||||
return string("linux");
|
||||
#else
|
||||
return genError("Failed to load platform details");
|
||||
#endif
|
||||
}
|
||||
|
||||
Maybe<string>
|
||||
DetailsResolver::Impl::getArch()
|
||||
{
|
||||
#if defined(arm32_rpi) || defined(arm32_musl) || defined(openwrt)
|
||||
auto architecture = DetailsResolvingHanlder::getCommandOutput("uname -a | awk '{print $(NF -1) }'");
|
||||
#else // not arm32_rpi || arm32_musl || openwrt
|
||||
auto architecture = DetailsResolvingHanlder::getCommandOutput("arch");
|
||||
#endif // defined(arm32_rpi) || defined(arm32_musl) || defined(openwrt)
|
||||
if (!architecture.ok()) return genError("Failed to load platform architecture, Error: " + architecture.getErr());
|
||||
return architecture;
|
||||
}
|
||||
|
||||
string
|
||||
DetailsResolver::Impl::getAgentVersion()
|
||||
{
|
||||
return Version::getFullVersion();
|
||||
}
|
||||
|
||||
bool
|
||||
DetailsResolver::Impl::isReverseProxy()
|
||||
{
|
||||
#if defined(gaia) || defined(smb)
|
||||
auto is_reverse_proxy = DetailsResolvingHanlder::getCommandOutput("cpprod_util CPPROD_IsConfigured CPwaap");
|
||||
if (is_reverse_proxy.ok()) {
|
||||
return is_reverse_proxy.unpack().front() == '1';
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
DetailsResolver::Impl::isKernelVersion3OrHigher()
|
||||
{
|
||||
static const string cmd =
|
||||
"clish -c 'show version os kernel' | awk '{print $4}' "
|
||||
"| cut -d '.' -f 1 | awk -F: '{ if ( $1 >= 3 ) {print 1} else {print 0}}'";
|
||||
|
||||
auto is_gogo = DetailsResolvingHanlder::getCommandOutput(cmd);
|
||||
if (is_gogo.ok()) {
|
||||
return is_gogo.unpack().front() == '1';
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
DetailsResolver::Impl::isGwNotVsx()
|
||||
{
|
||||
static const string is_gw_cmd = "cpprod_util FwIsFirewallModule";
|
||||
static const string is_vsx_cmd = "cpprod_util FWisVSX";
|
||||
auto is_gw = DetailsResolvingHanlder::getCommandOutput(is_gw_cmd);
|
||||
auto is_vsx = DetailsResolvingHanlder::getCommandOutput(is_vsx_cmd);
|
||||
if (is_gw.ok() && is_vsx.ok()) {
|
||||
return is_gw.unpack().front() == '1' && is_vsx.unpack().front() == '0';
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(gaia) || defined(smb)
|
||||
bool
|
||||
DetailsResolver::Impl::compareCheckpointVersion(int cp_version, std::function<bool(int, int)> compare_operator) const
|
||||
{
|
||||
int curr_version = getCheckpointVersion();
|
||||
return compare_operator(curr_version, cp_version);
|
||||
}
|
||||
|
||||
int
|
||||
DetailsResolver::Impl::getCheckpointVersion() const
|
||||
{
|
||||
#ifdef gaia
|
||||
static const string cmd =
|
||||
"echo $CPDIR | awk -F'-' '{print $NF}' | cut -c 2- |"
|
||||
" awk -F'.' '{ if( NF == 1 ) {print $1\"00\"} else {print $1$2} }'";
|
||||
#else // smb
|
||||
static const string cmd = "sqlcmd 'select major,minor from cpver' |"
|
||||
"awk '{if ($1 == \"major\") v += (substr($3,2) * 100);"
|
||||
" if ($1 == \"minor\") v += $3; } END { print v}'";
|
||||
|
||||
#endif // gaia
|
||||
auto version_out = DetailsResolvingHanlder::getCommandOutput(cmd);
|
||||
int cp_version = 0;
|
||||
if (version_out.ok()) {
|
||||
stringstream version_stream(version_out.unpack());
|
||||
version_stream >> cp_version;
|
||||
}
|
||||
return cp_version;
|
||||
}
|
||||
#endif // gaia || smb
|
||||
|
||||
bool
|
||||
DetailsResolver::Impl::isVersionEqualOrAboveR8110()
|
||||
{
|
||||
#if defined(gaia) || defined(smb)
|
||||
return compareCheckpointVersion(8110, std::greater_equal<int>());
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
Maybe<tuple<string, string, string>>
|
||||
DetailsResolver::Impl::parseNginxMetadata()
|
||||
{
|
||||
auto output_path = getConfigurationWithDefault<string>(
|
||||
"/tmp/nginx_meta_data.txt",
|
||||
"orchestration",
|
||||
"Nginx metadata temp file"
|
||||
);
|
||||
const string srcipt_exe_cmd =
|
||||
getFilesystemPathConfig() +
|
||||
"/scripts/cp-nano-makefile-generator.sh -f -o " +
|
||||
output_path;
|
||||
|
||||
dbgTrace(D_ORCHESTRATOR) << "Details resolver, srcipt exe cmd: " << srcipt_exe_cmd;
|
||||
auto is_nginx_exist = DetailsResolvingHanlder::getCommandOutput("which nginx");
|
||||
if (!is_nginx_exist.ok() || is_nginx_exist.unpack().size() == 0) {
|
||||
return genError("Nginx isn't installed");
|
||||
}
|
||||
|
||||
auto script_output = DetailsResolvingHanlder::getCommandOutput(srcipt_exe_cmd);
|
||||
if (!script_output.ok()) {
|
||||
return genError("Failed to generate nginx metadata, Error: " + script_output.getErr());
|
||||
}
|
||||
|
||||
I_OrchestrationTools *orchestration_tools = Singleton::Consume<I_OrchestrationTools>::by<DetailsResolver>();
|
||||
if (!orchestration_tools->doesFileExist(output_path)) {
|
||||
return genError("Failed to access nginx metadata file.");
|
||||
}
|
||||
|
||||
vector<string> lines;
|
||||
try {
|
||||
ifstream input_stream(output_path);
|
||||
if (!input_stream) {
|
||||
return genError("Cannot open the file with nginx metadata, File: " + output_path);
|
||||
}
|
||||
|
||||
string line;
|
||||
while (getline(input_stream, line)) {
|
||||
lines.push_back(line);
|
||||
}
|
||||
input_stream.close();
|
||||
|
||||
orchestration_tools->removeFile(output_path);
|
||||
} catch (const ifstream::failure &exception) {
|
||||
dbgWarning(D_ORCHESTRATOR)
|
||||
<< "Cannot read the file with required nginx metadata."
|
||||
<< " File: " << output_path
|
||||
<< " Error: " << exception.what();
|
||||
}
|
||||
|
||||
if (lines.size() == 0) return genError("Failed to read nginx metadata file");
|
||||
string nginx_version;
|
||||
string config_opt;
|
||||
string cc_opt;
|
||||
|
||||
for(string &line : lines) {
|
||||
if (line.size() == 0) continue;
|
||||
if (line.find("RELEASE_VERSION") != string::npos) continue;
|
||||
if (line.find("--with-cc=") != string::npos) continue;
|
||||
if (line.find("NGINX_VERSION") != string::npos) {
|
||||
auto eq_index = line.find("=");
|
||||
nginx_version = "nginx-" + line.substr(eq_index + 1);
|
||||
continue;
|
||||
}
|
||||
if (line.find("EXTRA_CC_OPT") != string::npos) {
|
||||
auto eq_index = line.find("=");
|
||||
cc_opt = line.substr(eq_index + 1);
|
||||
continue;
|
||||
}
|
||||
if (line.find("CONFIGURE_OPT") != string::npos) continue;
|
||||
if (line.back() == '\\') line.pop_back();
|
||||
config_opt += line;
|
||||
}
|
||||
return make_tuple(config_opt, cc_opt, nginx_version);
|
||||
}
|
||||
|
||||
DetailsResolver::DetailsResolver() : Component("DetailsResolver"), pimpl(make_unique<Impl>()) {}
|
||||
|
||||
DetailsResolver::~DetailsResolver() {}
|
||||
|
||||
void
|
||||
DetailsResolver::preload()
|
||||
{
|
||||
registerExpectedConfiguration<uint32_t>("orchestration", "Details resolver time out");
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
// 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 __CHECKPOINT_PRODUCT_HANDLERS_H__
|
||||
#define __CHECKPOINT_PRODUCT_HANDLERS_H__
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/regex.hpp>
|
||||
|
||||
#if defined(gaia) || defined(smb)
|
||||
Maybe<string>
|
||||
checkHasSDWan(const string &command_output)
|
||||
{
|
||||
if (command_output.front() == '1') return string("true");
|
||||
|
||||
return genError("Current host does not have SDWAN capability");
|
||||
}
|
||||
|
||||
Maybe<string>
|
||||
getMgmtObjType(const string &command_output)
|
||||
{
|
||||
if (!command_output.empty()) {
|
||||
if (command_output[0] == '1') return string("management");
|
||||
if (command_output[0] == '0') return string("gateway");
|
||||
}
|
||||
|
||||
return genError("Object type was not found");
|
||||
}
|
||||
|
||||
Maybe<string>
|
||||
chopHeadAndTail(const string &str, const string &prefix, const string &suffix)
|
||||
{
|
||||
if (str.size() < prefix.size() + suffix.size()) return genError("String too short");
|
||||
if (str.compare(0, prefix.size(), prefix)) return genError("Prefix mismatch");
|
||||
if (str.compare(str.size() - suffix.size(), suffix.size(), suffix)) return genError("Suffix mismatch");
|
||||
|
||||
return str.substr(prefix.size(), str.size() - prefix.size() - suffix.size());
|
||||
}
|
||||
|
||||
Maybe<string>
|
||||
getMgmtObjAttr(shared_ptr<istream> file_stream, const string &attr)
|
||||
{
|
||||
string line;
|
||||
while (getline(*file_stream, line)) {
|
||||
size_t attr_pos = line.find(attr);
|
||||
if (attr_pos == string::npos) continue;
|
||||
line = line.substr(attr_pos + attr.size());
|
||||
return chopHeadAndTail(line, "(", ")");
|
||||
}
|
||||
return genError("Object attribute was not found. Attr: " + attr);
|
||||
}
|
||||
|
||||
Maybe<string>
|
||||
getMgmtObjUid(shared_ptr<istream> file_stream)
|
||||
{
|
||||
return getMgmtObjAttr(file_stream, "uuid ");
|
||||
}
|
||||
|
||||
Maybe<string>
|
||||
getMgmtObjName(shared_ptr<istream> file_stream)
|
||||
{
|
||||
return getMgmtObjAttr(file_stream, "name ");
|
||||
}
|
||||
|
||||
Maybe<string>
|
||||
getMgmtParentObjAttr(shared_ptr<istream> file_stream, const string &parent_obj, const string &attr)
|
||||
{
|
||||
string line;
|
||||
bool found_parent_obj = false;
|
||||
while (getline(*file_stream, line)) {
|
||||
size_t parent_obj_pos = line.find(parent_obj);
|
||||
if (parent_obj_pos != string::npos) found_parent_obj = true;
|
||||
if (!found_parent_obj) continue;
|
||||
|
||||
size_t attr_pos = line.find(attr);
|
||||
if (attr_pos == string::npos) continue;
|
||||
line = line.substr(attr_pos + attr.size());
|
||||
return line;
|
||||
}
|
||||
return genError("Parent object attribute was not found. Attr: " + attr);
|
||||
}
|
||||
|
||||
Maybe<string>
|
||||
getMgmtParentObjUid(shared_ptr<istream> file_stream)
|
||||
{
|
||||
auto maybe_unparsed_uid = getMgmtParentObjAttr(file_stream, "cluster_object", "Uid ");
|
||||
if (!maybe_unparsed_uid.ok()) {
|
||||
return maybe_unparsed_uid;
|
||||
}
|
||||
const string &unparsed_uid = maybe_unparsed_uid.unpack();
|
||||
auto maybe_uid = chopHeadAndTail(unparsed_uid, "(\"{", "}\")");
|
||||
if (!maybe_uid.ok()) {
|
||||
return maybe_uid;
|
||||
}
|
||||
string uid = maybe_uid.unpack();
|
||||
transform(uid.begin(), uid.end(), uid.begin(), ::tolower);
|
||||
return uid;
|
||||
}
|
||||
|
||||
Maybe<string>
|
||||
getMgmtParentObjName(shared_ptr<istream> file_stream)
|
||||
{
|
||||
auto maybe_unparsed_name = getMgmtParentObjAttr(file_stream, "cluster_object", "Name ");
|
||||
if (!maybe_unparsed_name.ok()) {
|
||||
return maybe_unparsed_name;
|
||||
}
|
||||
const string &unparsed_name = maybe_unparsed_name.unpack();
|
||||
return chopHeadAndTail(unparsed_name, "(", ")");
|
||||
}
|
||||
#endif // gaia || smb
|
||||
|
||||
Maybe<string>
|
||||
getOsRelease(shared_ptr<istream> file_stream)
|
||||
{
|
||||
string line;
|
||||
while (getline(*file_stream, line)) {
|
||||
if (line.find("Check Point") != string::npos) return line;
|
||||
|
||||
static const string prety_name_attr = "PRETTY_NAME=";
|
||||
size_t pretty_name_idx = line.find(prety_name_attr);
|
||||
if (pretty_name_idx == string::npos) continue;
|
||||
line = line.substr(pretty_name_idx + prety_name_attr.size());
|
||||
if (line.front() == '"') line.erase(0, 1);
|
||||
if (line.back() == '"') line.pop_back();
|
||||
return line;
|
||||
}
|
||||
|
||||
return genError("Os release was not found");
|
||||
}
|
||||
|
||||
#if defined(alpine)
|
||||
string &
|
||||
ltrim(string &s)
|
||||
{
|
||||
auto it = find_if(
|
||||
s.begin(),
|
||||
s.end(),
|
||||
[](char c) { return !isspace<char>(c, locale::classic()); }
|
||||
);
|
||||
s.erase(s.begin(), it);
|
||||
return s;
|
||||
}
|
||||
|
||||
string &
|
||||
rtrim(string &s)
|
||||
{
|
||||
auto it = find_if(
|
||||
s.rbegin(),
|
||||
s.rend(),
|
||||
[](char c) { return !isspace<char>(c, locale::classic()); }
|
||||
);
|
||||
s.erase(it.base(), s.end());
|
||||
return s;
|
||||
}
|
||||
|
||||
string &
|
||||
trim(string &s)
|
||||
{
|
||||
return ltrim(rtrim(s));
|
||||
}
|
||||
|
||||
Maybe<string>
|
||||
getCPAlpineTag(shared_ptr<istream> file_stream)
|
||||
{
|
||||
string line;
|
||||
while (getline(*file_stream, line)) {
|
||||
if (trim(line) != "") return line;
|
||||
}
|
||||
return genError("Alpine tag was not found");
|
||||
}
|
||||
#endif // alpine
|
||||
|
||||
#endif // __CHECKPOINT_PRODUCT_HANDLERS_H__
|
||||
@@ -0,0 +1,72 @@
|
||||
// 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 __DETAILS_RESOLVER_HANDLER_CC__
|
||||
#error details_resolver_handlers/details_resolver_impl.h should not be included directly.
|
||||
#endif // __DETAILS_RESOLVER_HANDLER_CC__
|
||||
|
||||
// use SHELL_CMD_HANDLER(key as string, shell command as string, ptr to Maybe<string> handler(const string&))
|
||||
// to return a string value for an attribute key based on a logic executed in a handler that receives
|
||||
// shell command execution output as its input
|
||||
#ifdef SHELL_CMD_HANDLER
|
||||
|
||||
#if defined(gaia) || defined(smb)
|
||||
SHELL_CMD_HANDLER("cpProductIntegrationMgmtObjectType", "cpprod_util CPPROD_IsMgmtMachine", getMgmtObjType)
|
||||
SHELL_CMD_HANDLER("hasSDWan", "[ -f $FWDIR/bin/sdwan_steering ] && echo '1' || echo '0'", checkHasSDWan)
|
||||
#endif //gaia
|
||||
|
||||
#endif // SHELL_CMD_HANDLER
|
||||
|
||||
|
||||
// use SHELL_CMD_OUTPUT(key as string, shell command as string) to return a shell command output as the value
|
||||
// for a given key
|
||||
#ifdef SHELL_CMD_OUTPUT
|
||||
SHELL_CMD_OUTPUT("kernel_version", "uname -r")
|
||||
SHELL_CMD_OUTPUT("helloWorld", "cat /tmp/agentHelloWorld 2>/dev/null")
|
||||
#endif // SHELL_CMD_OUTPUT
|
||||
|
||||
|
||||
// use FILE_CONTENT_HANDLER(key as string, path to file as string, ptr to Maybe<string> handler(ifstream&))
|
||||
// to return a string value for an attribute key based on a logic executed in a handler that receives file as input
|
||||
#ifdef FILE_CONTENT_HANDLER
|
||||
|
||||
#if defined(alpine)
|
||||
FILE_CONTENT_HANDLER("alpine_tag", "/usr/share/build/cp-alpine-tag", getCPAlpineTag)
|
||||
#endif // alpine
|
||||
#if defined(gaia) || defined(smb)
|
||||
FILE_CONTENT_HANDLER("os_release", "/etc/cp-release", getOsRelease)
|
||||
FILE_CONTENT_HANDLER(
|
||||
"cpProductIntegrationMgmtObjectUid",
|
||||
(getenv("FWDIR") ? string(getenv("FWDIR")) : "") + "/database/myown.C",
|
||||
getMgmtObjUid
|
||||
)
|
||||
FILE_CONTENT_HANDLER(
|
||||
"cpProductIntegrationMgmtObjectName",
|
||||
(getenv("FWDIR") ? string(getenv("FWDIR")) : "") + "/database/myown.C",
|
||||
getMgmtObjName
|
||||
)
|
||||
FILE_CONTENT_HANDLER(
|
||||
"cpProductIntegrationMgmtParentObjectUid",
|
||||
(getenv("FWDIR") ? string(getenv("FWDIR")) : "") + "/database/myself_objects.C",
|
||||
getMgmtParentObjUid
|
||||
)
|
||||
FILE_CONTENT_HANDLER(
|
||||
"cpProductIntegrationMgmtParentObjectName",
|
||||
(getenv("FWDIR") ? string(getenv("FWDIR")) : "") + "/database/myself_objects.C",
|
||||
getMgmtParentObjName
|
||||
)
|
||||
#else // !(gaia || smb)
|
||||
FILE_CONTENT_HANDLER("os_release", "/etc/os-release", getOsRelease)
|
||||
#endif // gaia || smb
|
||||
|
||||
#endif // FILE_CONTENT_HANDLER
|
||||
@@ -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 "details_resolving_handler.h"
|
||||
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
#include "maybe_res.h"
|
||||
#include "enum_array.h"
|
||||
#include "i_shell_cmd.h"
|
||||
#include "config.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
USE_DEBUG_FLAG(D_AGENT_DETAILS);
|
||||
|
||||
using ShellCommandHandler = function<Maybe<string>(const string &raw_otput)>;
|
||||
using FileContentHandler = function<Maybe<string>(shared_ptr<istream> file_otput)>;
|
||||
|
||||
#define __DETAILS_RESOLVER_HANDLER_CC__
|
||||
|
||||
#include "checkpoint_product_handlers.h"
|
||||
|
||||
class DetailsResolvingHanlder::Impl
|
||||
{
|
||||
public:
|
||||
map<string, string> getResolvedDetails() const;
|
||||
static Maybe<string> getCommandOutput(const string &cmd);
|
||||
|
||||
private:
|
||||
#define SHELL_CMD_OUTPUT(ATTRIBUTE, COMMAND) SHELL_CMD_HANDLER(ATTRIBUTE, COMMAND, [](const string &s) { return s; })
|
||||
#define SHELL_CMD_HANDLER(ATTRIBUTE, COMMAND, HANDLER) {ATTRIBUTE, {COMMAND, ShellCommandHandler(HANDLER)}},
|
||||
map<string, pair<string, ShellCommandHandler>> shell_command_handlers = {
|
||||
#include "details_resolver_impl.h"
|
||||
};
|
||||
#undef SHELL_CMD_OUTPUT
|
||||
#undef SHELL_CMD_HANDLER
|
||||
|
||||
#define FILE_CONTENT_HANDLER(ATTRIBUTE, FILE, HANDLER) {ATTRIBUTE, {FILE, FileContentHandler(HANDLER)}},
|
||||
map<string, pair<string, FileContentHandler>> file_content_handlers = {
|
||||
#include "details_resolver_impl.h"
|
||||
};
|
||||
#undef FILE_CONTENT_HANDLER
|
||||
};
|
||||
|
||||
map<string, string>
|
||||
DetailsResolvingHanlder::Impl::getResolvedDetails() const
|
||||
{
|
||||
map<string, string> resolved_details;
|
||||
for (auto shell_handler : shell_command_handlers) {
|
||||
const string &attr = shell_handler.first;
|
||||
const string &command = shell_handler.second.first;
|
||||
ShellCommandHandler handler = shell_handler.second.second;
|
||||
|
||||
Maybe<string> shell_command_output = getCommandOutput(command);
|
||||
if (!shell_command_output.ok()) continue;
|
||||
Maybe<string> handler_ret = handler(*shell_command_output);
|
||||
if (handler_ret.ok()) resolved_details[attr] = *handler_ret;
|
||||
}
|
||||
|
||||
for (auto file_handler : file_content_handlers) {
|
||||
const string &attr = file_handler.first;
|
||||
const string &path = file_handler.second.first;
|
||||
FileContentHandler handler = file_handler.second.second;
|
||||
|
||||
shared_ptr<ifstream> in_file = make_shared<ifstream>(path);
|
||||
if (!in_file->is_open()) {
|
||||
dbgWarning(D_AGENT_DETAILS) << "Could not open file for processing. Path: " << path;
|
||||
continue;
|
||||
}
|
||||
|
||||
dbgDebug(D_AGENT_DETAILS) << "Successfully opened file for processing. Path: " << path;
|
||||
if (in_file->peek() != ifstream::traits_type::eof()) {
|
||||
Maybe<string> handler_ret = handler(in_file);
|
||||
if (handler_ret.ok()) resolved_details[attr] = *handler_ret;
|
||||
}
|
||||
in_file->close();
|
||||
}
|
||||
|
||||
I_AgentDetailsReporter *reporter = Singleton::Consume<I_AgentDetailsReporter>::by<DetailsResolvingHanlder>();
|
||||
reporter->addAttr(resolved_details);
|
||||
|
||||
return resolved_details;
|
||||
}
|
||||
|
||||
Maybe<string>
|
||||
DetailsResolvingHanlder::Impl::getCommandOutput(const string &cmd)
|
||||
{
|
||||
I_ShellCmd *shell = Singleton::Consume<I_ShellCmd>::by<DetailsResolvingHanlder>();
|
||||
uint32_t timeout = getConfigurationWithDefault<uint32_t>(5000, "orchestration", "Details resolver time out");
|
||||
auto result = shell->getExecOutput(cmd, timeout);
|
||||
if (!result.ok()) return result;
|
||||
|
||||
auto unpacked_result = result.unpack();
|
||||
if (unpacked_result.back() == '\n') unpacked_result.pop_back();
|
||||
|
||||
return unpacked_result;
|
||||
}
|
||||
|
||||
DetailsResolvingHanlder::DetailsResolvingHanlder() : pimpl(make_unique<Impl>()) {}
|
||||
DetailsResolvingHanlder::~DetailsResolvingHanlder() {}
|
||||
|
||||
|
||||
map<string, string>
|
||||
DetailsResolvingHanlder::getResolvedDetails() const
|
||||
{
|
||||
return pimpl->getResolvedDetails();
|
||||
}
|
||||
|
||||
Maybe<string>
|
||||
DetailsResolvingHanlder::getCommandOutput(const string &cmd)
|
||||
{
|
||||
return DetailsResolvingHanlder::Impl::getCommandOutput(cmd);
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef __DETAILS_RESOLVING_HANDLER_H__
|
||||
#define __DETAILS_RESOLVING_HANDLER_H__
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include "i_shell_cmd.h"
|
||||
#include "i_agent_details_reporter.h"
|
||||
|
||||
class DetailsResolvingHanlder
|
||||
:
|
||||
Singleton::Consume<I_ShellCmd>,
|
||||
Singleton::Consume<I_AgentDetailsReporter>
|
||||
{
|
||||
public:
|
||||
DetailsResolvingHanlder();
|
||||
~DetailsResolvingHanlder();
|
||||
|
||||
std::map<std::string, std::string> getResolvedDetails() const;
|
||||
|
||||
static Maybe<std::string> getCommandOutput(const std::string &cmd);
|
||||
|
||||
private:
|
||||
class Impl;
|
||||
std::unique_ptr<Impl> pimpl;
|
||||
};
|
||||
|
||||
#endif // __DETAILS_RESOLVING_HANDLER_H__
|
||||
5
components/security_apps/orchestration/downloader/CMakeLists.txt
Executable file
5
components/security_apps/orchestration/downloader/CMakeLists.txt
Executable file
@@ -0,0 +1,5 @@
|
||||
ADD_DEFINITIONS(-Wno-deprecated-declarations -Dalpine)
|
||||
|
||||
add_library(orchestration_downloader curl_client.cc downloader.cc http_client.cc https_client.cc)
|
||||
|
||||
add_subdirectory(downloader_ut)
|
||||
438
components/security_apps/orchestration/downloader/curl_client.cc
Executable file
438
components/security_apps/orchestration/downloader/curl_client.cc
Executable file
@@ -0,0 +1,438 @@
|
||||
// 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 "curl_client.h"
|
||||
|
||||
#include <curl/curl.h>
|
||||
#if defined(alpine)
|
||||
#include <openssl/ossl_typ.h>
|
||||
#else
|
||||
#include <openssl/types.h>
|
||||
#endif //ifdef alpine
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/x509_vfy.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <memory>
|
||||
#include <algorithm>
|
||||
|
||||
#include "config.h"
|
||||
#include "url_parser.h"
|
||||
#include "debug.h"
|
||||
#include "sasal.h"
|
||||
#include "scope_exit.h"
|
||||
|
||||
USE_DEBUG_FLAG(D_HTTP_REQUEST);
|
||||
|
||||
using namespace std;
|
||||
|
||||
SASAL_START // Orchestration - Communication
|
||||
// LCOV_EXCL_START Reason: Depends on real download server.
|
||||
|
||||
class CurlGlobalInit
|
||||
{
|
||||
public:
|
||||
CurlGlobalInit() { curl_global_init(CURL_GLOBAL_DEFAULT); }
|
||||
~CurlGlobalInit() { curl_global_cleanup(); }
|
||||
};
|
||||
static CurlGlobalInit global_curl_handle;
|
||||
|
||||
HttpCurl::HttpCurl(
|
||||
const URLParser &_url,
|
||||
ofstream &_out_file,
|
||||
const string &_bearer,
|
||||
const Maybe<string> &proxy_url,
|
||||
const Maybe<uint16_t> &proxy_port,
|
||||
const Maybe<string> &proxy_auth)
|
||||
:
|
||||
url(_url),
|
||||
out_file(_out_file),
|
||||
bearer(_bearer),
|
||||
curl(unique_ptr<CURL, function<void(CURL *)>>(curl_easy_init(), curl_easy_cleanup))
|
||||
{
|
||||
string port = url.getPort();
|
||||
if (!port.empty())
|
||||
{
|
||||
curl_url = url.getBaseURL().unpack() + ":" + port + url.getQuery();
|
||||
} else
|
||||
{
|
||||
curl_url = url.getBaseURL().unpack() + url.getQuery();
|
||||
}
|
||||
|
||||
if (proxy_url.ok())
|
||||
{
|
||||
//Update curl proxy
|
||||
if (!proxy_port.ok())
|
||||
{
|
||||
dbgWarning(D_HTTP_REQUEST)
|
||||
<< "Invalid proxy port, CURL default port will be used instead. Error: "
|
||||
<< proxy_port.getErr();
|
||||
proxy = proxy_url.unpack();
|
||||
} else
|
||||
{
|
||||
proxy = proxy_url.unpack() + ":" + to_string(proxy_port.unpack());
|
||||
}
|
||||
}
|
||||
if (proxy_auth.ok())
|
||||
{
|
||||
I_Encryptor *encryptor = Singleton::Consume<I_Encryptor>::by<HttpCurl>();
|
||||
proxy_credentials = "Proxy-Authorization: Basic " + encryptor->base64Encode(proxy_auth.unpack());
|
||||
}
|
||||
}
|
||||
|
||||
HttpCurl::HttpCurl(const HttpCurl &other)
|
||||
:
|
||||
url(other.url),
|
||||
out_file(other.out_file),
|
||||
bearer(other.bearer),
|
||||
proxy(other.proxy),
|
||||
proxy_credentials(other.proxy_credentials),
|
||||
curl(unique_ptr<CURL, function<void(CURL *)>>(curl_easy_init(), curl_easy_cleanup))
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
HttpCurl::setCurlOpts(long timeout, HTTP_VERSION http_version)
|
||||
{
|
||||
struct curl_slist *headers = NULL;
|
||||
struct curl_slist *proxy_headers = NULL;
|
||||
CURL *curl_handle = curl.get();
|
||||
|
||||
//HTTP options
|
||||
curl_easy_setopt(curl_handle, CURLOPT_HTTP_VERSION, http_version);
|
||||
|
||||
//SSL options
|
||||
curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L);
|
||||
|
||||
//Header options
|
||||
curl_easy_setopt(curl_handle, CURLOPT_URL, curl_url.c_str());
|
||||
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, writeResponseCallback);
|
||||
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, &out_file);
|
||||
curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, timeout);
|
||||
headers = curl_slist_append(headers, "Accept: */*");
|
||||
string auth = string("Authorization: Bearer ") + bearer;
|
||||
headers = curl_slist_append(headers, auth.c_str());
|
||||
headers = curl_slist_append(headers, "User-Agent: Infinity Next (a7030abf93a4c13)");
|
||||
string uuid_header = string("X-Trace-Id: ") + TraceIdGenerator::generateTraceId();
|
||||
headers = curl_slist_append(headers, "Connection: close");
|
||||
headers = curl_slist_append(headers, uuid_header.c_str());
|
||||
|
||||
//Proxy options
|
||||
if (!proxy.empty())
|
||||
{
|
||||
curl_easy_setopt(curl_handle, CURLOPT_PROXY, proxy.c_str());
|
||||
if (!proxy_credentials.empty())
|
||||
{
|
||||
proxy_headers = curl_slist_append(proxy_headers, proxy_credentials.c_str());
|
||||
//Apply proxy headers
|
||||
curl_easy_setopt(curl_handle, CURLOPT_PROXYHEADER, proxy_headers);
|
||||
}
|
||||
dbgTrace(D_HTTP_REQUEST) << "Using Proxy: " << proxy;
|
||||
}
|
||||
|
||||
//Apply headers
|
||||
curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headers);
|
||||
}
|
||||
|
||||
bool
|
||||
HttpCurl::connect()
|
||||
{
|
||||
// Response information.
|
||||
long http_code;
|
||||
char errorstr[CURL_ERROR_SIZE];
|
||||
CURLcode res;
|
||||
stringstream response_header;
|
||||
|
||||
CURL *curl_handle = curl.get();
|
||||
|
||||
auto __scope_exit = make_scope_exit(
|
||||
[this] () {
|
||||
out_file.flush();
|
||||
out_file.close();
|
||||
}
|
||||
);
|
||||
|
||||
//Debug options
|
||||
curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);
|
||||
curl_easy_setopt(curl_handle, CURLOPT_DEBUGFUNCTION, trace_http_request);
|
||||
curl_easy_setopt(curl_handle, CURLOPT_DEBUGDATA, static_cast<void*>(&response_header));
|
||||
curl_easy_setopt(curl_handle, CURLOPT_ERRORBUFFER, errorstr);
|
||||
|
||||
// Perform the request, res will get the return code
|
||||
res = curl_easy_perform(curl_handle);
|
||||
if (res != CURLE_OK) {
|
||||
dbgWarning(D_HTTP_REQUEST) << "Failed to perform CURL request. CURL error " << string(errorstr);
|
||||
dbgWarning(D_HTTP_REQUEST) << "CURL result " + string(curl_easy_strerror(res));
|
||||
print_response_header(response_header);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &http_code);
|
||||
|
||||
if (http_code != 200){
|
||||
dbgWarning(D_HTTP_REQUEST) << "Failed to connect. Error code: " + to_string(http_code);
|
||||
print_response_header(response_header);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
dbgTrace(D_HTTP_REQUEST) << "CURL HTTP request successfully completed.";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
HttpCurl::trace_http_request(
|
||||
CURL *,
|
||||
curl_infotype type,
|
||||
char *data,
|
||||
size_t,
|
||||
void *opq)
|
||||
{
|
||||
stringstream *response_header = static_cast<stringstream *>(opq);
|
||||
switch (type)
|
||||
{
|
||||
case CURLINFO_HEADER_OUT:
|
||||
dbgTrace(D_HTTP_REQUEST)
|
||||
<< "=> Sending the following HTTP request:\n"
|
||||
<< string(data);
|
||||
break;
|
||||
case CURLINFO_HEADER_IN:
|
||||
if (!response_header)
|
||||
{
|
||||
// Should never reach this if. But just in case.
|
||||
// The data will be printed at chunks in this case
|
||||
dbgError(D_HTTP_REQUEST)
|
||||
<< "<= Received the following HTTP response header (should not reach here):\n"
|
||||
<< string(data);
|
||||
} else
|
||||
{
|
||||
// The response header Will be printed at once later after curl_easy_perform.
|
||||
// And after assembling all the response header chunks.
|
||||
*response_header << string(data);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u_int
|
||||
HttpCurl::writeResponseCallback(
|
||||
const char *in_buf,
|
||||
uint num_of_messages,
|
||||
uint size_of_data,
|
||||
ostream out_stream)
|
||||
{
|
||||
const unsigned long total_bytes(num_of_messages * size_of_data);
|
||||
out_stream.write(in_buf, total_bytes);
|
||||
return total_bytes;
|
||||
}
|
||||
|
||||
void
|
||||
HttpCurl::print_response_header(stringstream &stream)
|
||||
{
|
||||
string line;
|
||||
istringstream header_stream(stream.str());
|
||||
stringstream header_lines;
|
||||
int lines_to_print = 10;
|
||||
int i = 0;
|
||||
|
||||
while (getline(header_stream, line) && i < lines_to_print) {
|
||||
header_lines << line << '\n';
|
||||
++i;
|
||||
}
|
||||
|
||||
dbgWarning(D_HTTP_REQUEST)
|
||||
<< "<= Received the following HTTP response header:\n"
|
||||
<< header_lines.str();
|
||||
}
|
||||
|
||||
HttpsCurl::HttpsCurl(
|
||||
const URLParser &_url,
|
||||
ofstream &_out_file,
|
||||
const string &_bearer,
|
||||
const Maybe<string> &proxy_url,
|
||||
const Maybe<uint16_t> &proxy_port,
|
||||
const Maybe<string> &proxy_auth,
|
||||
const string &_ca_path) :
|
||||
HttpCurl(_url, _out_file, _bearer, proxy_url, proxy_port, proxy_auth),
|
||||
ca_path(_ca_path) {}
|
||||
|
||||
HttpsCurl::HttpsCurl(const HttpsCurl &other) :
|
||||
HttpCurl(other),
|
||||
ca_path(other.ca_path) {}
|
||||
|
||||
void
|
||||
HttpsCurl::setCurlOpts(long timeout, HTTP_VERSION http_version)
|
||||
{
|
||||
struct curl_slist *headers = NULL;
|
||||
struct curl_slist *proxy_headers = NULL;
|
||||
CURL *curl_handle = curl.get();
|
||||
|
||||
URLProtocol protocol = url.getProtocol();
|
||||
if (protocol == URLProtocol::HTTPS)
|
||||
{
|
||||
if (curl_url.find("https://") == string::npos)
|
||||
{
|
||||
//Append https://
|
||||
curl_url = "https://" + curl_url;
|
||||
}
|
||||
}
|
||||
|
||||
//HTTP options
|
||||
curl_easy_setopt(curl_handle, CURLOPT_HTTP_VERSION, http_version);
|
||||
|
||||
//SSL options
|
||||
if (getProfileAgentSettingWithDefault<bool>(
|
||||
false,
|
||||
"agent.config.message.ignoreSslValidation") == false)
|
||||
{
|
||||
curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 1L);
|
||||
curl_easy_setopt(curl_handle, CURLOPT_SSL_CTX_FUNCTION, ssl_ctx_verify_certificate);
|
||||
curl_easy_setopt(curl_handle, CURLOPT_SSL_CTX_DATA, static_cast<void*>(&bearer));
|
||||
} else
|
||||
{
|
||||
curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L);
|
||||
dbgWarning(D_HTTP_REQUEST) << "Ignoring SSL validation";
|
||||
}
|
||||
|
||||
//Header options
|
||||
curl_easy_setopt(curl_handle, CURLOPT_URL, curl_url.c_str());
|
||||
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, writeResponseCallback);
|
||||
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, &out_file);
|
||||
curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, timeout);
|
||||
curl_easy_setopt(curl_handle, CURLOPT_CAINFO, ca_path.c_str());
|
||||
headers = curl_slist_append(headers, "Accept: */*");
|
||||
string auth = string("Authorization: Bearer ") + bearer;
|
||||
headers = curl_slist_append(headers, auth.c_str());
|
||||
headers = curl_slist_append(headers, "User-Agent: Infinity Next (a7030abf93a4c13)");
|
||||
string uuid_header = string("X-Trace-Id: ") + TraceIdGenerator::generateTraceId();
|
||||
headers = curl_slist_append(headers, "Connection: close");
|
||||
headers = curl_slist_append(headers, uuid_header.c_str());
|
||||
|
||||
// Proxy options
|
||||
if (!proxy.empty())
|
||||
{
|
||||
curl_easy_setopt(curl_handle, CURLOPT_PROXY, proxy.c_str());
|
||||
if (!proxy_credentials.empty())
|
||||
{
|
||||
proxy_headers = curl_slist_append(proxy_headers, proxy_credentials.c_str());
|
||||
//Apply proxy headers
|
||||
curl_easy_setopt(curl_handle, CURLOPT_PROXYHEADER, proxy_headers);
|
||||
}
|
||||
dbgTrace(D_HTTP_REQUEST) << "Using Proxy : " << proxy;
|
||||
}
|
||||
|
||||
//Apply headers
|
||||
curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headers);
|
||||
}
|
||||
|
||||
int
|
||||
HttpsCurl::verify_certificate(int preverify_ok, X509_STORE_CTX *ctx)
|
||||
{
|
||||
switch (X509_STORE_CTX_get_error(ctx))
|
||||
{
|
||||
case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
|
||||
dbgWarning(D_HTTP_REQUEST) << "SSL verification error: X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT";
|
||||
break;
|
||||
case X509_V_ERR_CERT_NOT_YET_VALID:
|
||||
case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
|
||||
dbgWarning(D_HTTP_REQUEST) << "SSL verification error: Certificate not yet valid";
|
||||
break;
|
||||
case X509_V_ERR_CERT_HAS_EXPIRED:
|
||||
case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
|
||||
dbgWarning(D_HTTP_REQUEST) << "Certificate expired";
|
||||
break;
|
||||
case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
|
||||
case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
|
||||
dbgDebug(D_HTTP_REQUEST) << "Self signed certificate in chain";
|
||||
if (getConfigurationWithDefault(false, "orchestration", "Self signed certificates acceptable")) {
|
||||
preverify_ok = true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (!preverify_ok) {
|
||||
dbgWarning(D_HTTP_REQUEST)
|
||||
<< "Certificate verification error number: "
|
||||
<< X509_STORE_CTX_get_error(ctx);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return preverify_ok;
|
||||
}
|
||||
|
||||
CURLcode
|
||||
HttpsCurl::ssl_ctx_verify_certificate(CURL *, void *sslctx, void *opq)
|
||||
{
|
||||
SSL_CTX *ctx = (SSL_CTX *) sslctx;
|
||||
string *token_ptr = static_cast<string*>(opq);
|
||||
if(!token_ptr)
|
||||
{
|
||||
dbgWarning(D_HTTP_REQUEST) << "Invalid token (bearer) was received";
|
||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
}
|
||||
string token = *token_ptr;
|
||||
|
||||
if (token.empty())
|
||||
{
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
SSL_CTX_set_verify(
|
||||
ctx,
|
||||
SSL_VERIFY_FAIL_IF_NO_PEER_CERT | SSL_VERIFY_PEER,
|
||||
verify_certificate
|
||||
);
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
string
|
||||
TraceIdGenerator::generateRandomString(uint length)
|
||||
{
|
||||
string dst(length, 0);
|
||||
static thread_local mt19937 range(random_device{}());
|
||||
|
||||
auto randchar = [&]() -> char
|
||||
{
|
||||
static const char charset[] = "0123456789abcdefghijklmnopqrstuvwxyz";
|
||||
static const size_t size = (sizeof(charset) - 1);
|
||||
return charset[ range() % size ];
|
||||
};
|
||||
|
||||
generate_n(dst.begin(), length, randchar);
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
string
|
||||
TraceIdGenerator::generateTraceId()
|
||||
{
|
||||
string part1 = generateRandomString(8);
|
||||
string part2 = generateRandomString(4);
|
||||
string part3 = generateRandomString(4);
|
||||
string part4 = generateRandomString(4);
|
||||
string part5 = generateRandomString(12);
|
||||
return string(part1 + "-" + part2 + "-" + part3 + "-" + part4 + "-" + part5);
|
||||
}
|
||||
|
||||
SASAL_END
|
||||
115
components/security_apps/orchestration/downloader/curl_client.h
Executable file
115
components/security_apps/orchestration/downloader/curl_client.h
Executable file
@@ -0,0 +1,115 @@
|
||||
// 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 <curl/curl.h>
|
||||
|
||||
#if defined(alpine)
|
||||
#include <openssl/ossl_typ.h>
|
||||
#else
|
||||
#include <openssl/types.h>
|
||||
#endif //ifdef alpine
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <ostream>
|
||||
|
||||
#include "i_encryptor.h"
|
||||
#include "scope_exit.h"
|
||||
#include "url_parser.h"
|
||||
#include "sasal.h"
|
||||
|
||||
USE_DEBUG_FLAG(D_HTTP_REQUEST);
|
||||
|
||||
SASAL_START // Orchestration - Communication
|
||||
// LCOV_EXCL_START Reason: Depends on real download server.
|
||||
|
||||
enum class HTTP_VERSION
|
||||
{
|
||||
HTTP_VERSION_NONE = CURL_HTTP_VERSION_NONE, //libcurl will use whatever it thinks fit.
|
||||
HTTP_VERSION_1_0 = CURL_HTTP_VERSION_1_0,
|
||||
HTTP_VERSION_1_1 = CURL_HTTP_VERSION_1_1,
|
||||
HTTP_VERSION_2_0 = CURL_HTTP_VERSION_2_0
|
||||
};
|
||||
|
||||
class TraceIdGenerator
|
||||
{
|
||||
public:
|
||||
static std::string generateTraceId();
|
||||
private:
|
||||
static std::string generateRandomString(uint length);
|
||||
};
|
||||
|
||||
class HttpCurl : public Singleton::Consume<I_Encryptor>
|
||||
{
|
||||
public:
|
||||
HttpCurl(
|
||||
const URLParser &_url,
|
||||
std::ofstream &_out_file,
|
||||
const std::string &_bearer,
|
||||
const Maybe<std::string> &proxy_url,
|
||||
const Maybe<uint16_t> &proxy_port,
|
||||
const Maybe<std::string> &proxy_auth);
|
||||
|
||||
HttpCurl(const HttpCurl &other);
|
||||
|
||||
virtual void setCurlOpts(long timeout = 60L, HTTP_VERSION http_version = HTTP_VERSION::HTTP_VERSION_1_1);
|
||||
virtual bool connect();
|
||||
|
||||
protected:
|
||||
static int trace_http_request(
|
||||
CURL *handle,
|
||||
curl_infotype type,
|
||||
char *data,
|
||||
size_t size,
|
||||
void *userptr);
|
||||
static u_int writeResponseCallback(
|
||||
const char *in_buf,
|
||||
uint num_of_messages,
|
||||
uint size_of_data,
|
||||
std::ostream out_stream);
|
||||
void print_response_header(std::stringstream &stream);
|
||||
|
||||
const URLParser& url;
|
||||
std::ofstream &out_file;
|
||||
std::string bearer;
|
||||
std::string proxy;
|
||||
std::string proxy_credentials;
|
||||
std::unique_ptr<CURL, std::function<void(CURL *)>> curl;
|
||||
std::string curl_url;
|
||||
};
|
||||
|
||||
class HttpsCurl : public HttpCurl
|
||||
{
|
||||
public:
|
||||
HttpsCurl(
|
||||
const URLParser &_url,
|
||||
std::ofstream &_out_file,
|
||||
const std::string &_bearer,
|
||||
const Maybe<std::string> &proxy_url,
|
||||
const Maybe<uint16_t> &proxy_port,
|
||||
const Maybe<std::string> &proxy_auth,
|
||||
const std::string &_ca_path);
|
||||
|
||||
HttpsCurl(const HttpsCurl& other);
|
||||
|
||||
static CURLcode ssl_ctx_verify_certificate(CURL *curl, void *ssl_ctx, void *opq);
|
||||
static int verify_certificate(int preverify_ok, X509_STORE_CTX *ctx);
|
||||
void setCurlOpts(long timeout = 60L, HTTP_VERSION http_version = HTTP_VERSION::HTTP_VERSION_1_1) override;
|
||||
|
||||
private:
|
||||
std::string ca_path;
|
||||
};
|
||||
|
||||
SASAL_END
|
||||
387
components/security_apps/orchestration/downloader/downloader.cc
Executable file
387
components/security_apps/orchestration/downloader/downloader.cc
Executable file
@@ -0,0 +1,387 @@
|
||||
// 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 "downloader.h"
|
||||
|
||||
#include "i_orchestration_tools.h"
|
||||
#include "singleton.h"
|
||||
#include "http_client.h"
|
||||
#include "debug.h"
|
||||
#include "config.h"
|
||||
#include "rest.h"
|
||||
#include "sasal.h"
|
||||
#include "cereal/external/rapidjson/document.h"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
using namespace std;
|
||||
using namespace rapidjson;
|
||||
|
||||
SASAL_START // Orchestration - Communication
|
||||
|
||||
USE_DEBUG_FLAG(D_ORCHESTRATOR);
|
||||
|
||||
class Downloader::Impl : Singleton::Provide<I_Downloader>::From<Downloader>
|
||||
{
|
||||
public:
|
||||
void init();
|
||||
|
||||
Maybe<string> downloadFileFromFog(
|
||||
const string &checksum,
|
||||
Package::ChecksumTypes checksum_type,
|
||||
const GetResourceFile &resourse_file
|
||||
) const override;
|
||||
|
||||
Maybe<map<string, string>> downloadVirtualFileFromFog(
|
||||
const GetResourceFile &resourse_file,
|
||||
Package::ChecksumTypes checksum_type
|
||||
) const override;
|
||||
|
||||
Maybe<string> downloadFileFromURL(
|
||||
const string &url,
|
||||
const string &checksum,
|
||||
Package::ChecksumTypes checksum_type,
|
||||
const string &service_name
|
||||
) const override;
|
||||
|
||||
private:
|
||||
Maybe<string> downloadFileFromFogByHTTP(
|
||||
const GetResourceFile &resourse_file,
|
||||
const string &file_name
|
||||
) const;
|
||||
|
||||
Maybe<string> validateChecksum(
|
||||
const string &checksum,
|
||||
Package::ChecksumTypes checksum_type,
|
||||
Maybe<string> &file_path
|
||||
) const;
|
||||
|
||||
Maybe<string> getFileFromExternalURL(
|
||||
const URLParser &url,
|
||||
const string &file_name,
|
||||
bool auth_required
|
||||
) const;
|
||||
Maybe<string> getFileFromLocal(const string &local_file_path, const string &file_name) const;
|
||||
Maybe<string> getFileFromURL(const URLParser &url, const string &file_name, bool auth_required) const;
|
||||
|
||||
tuple<string, string> splitQuery(const string &query) const;
|
||||
string vectorToPath(const vector<string> &vec) const;
|
||||
string dir_path;
|
||||
};
|
||||
|
||||
void
|
||||
Downloader::Impl::init()
|
||||
{
|
||||
dir_path = getConfigurationWithDefault<string>(
|
||||
"/tmp/orchestration_downloads",
|
||||
"orchestration",
|
||||
"Default file download path"
|
||||
);
|
||||
|
||||
Singleton::Consume<I_OrchestrationTools>::by<Downloader>()->createDirectory(dir_path);
|
||||
}
|
||||
|
||||
Maybe<string>
|
||||
Downloader::Impl::downloadFileFromFog(
|
||||
const string &checksum,
|
||||
Package::ChecksumTypes checksum_type,
|
||||
const GetResourceFile &resourse_file) const
|
||||
{
|
||||
auto file_path = downloadFileFromFogByHTTP(resourse_file, resourse_file.getFileName() + ".download");
|
||||
|
||||
if (!file_path.ok()) {
|
||||
return file_path;
|
||||
}
|
||||
|
||||
auto checksum_validation = validateChecksum(checksum, checksum_type, file_path);
|
||||
if (!checksum_validation.ok()) return checksum_validation;
|
||||
|
||||
I_OrchestrationTools *orchestration_tools = Singleton::Consume<I_OrchestrationTools>::by<Downloader>();
|
||||
if (!orchestration_tools->isNonEmptyFile(file_path.unpack())) {
|
||||
return genError("Failed to download file " + resourse_file.getFileName());
|
||||
}
|
||||
|
||||
return file_path;
|
||||
}
|
||||
|
||||
Maybe<map<string, string>>
|
||||
Downloader::Impl::downloadVirtualFileFromFog(
|
||||
const GetResourceFile &resourse_file,
|
||||
Package::ChecksumTypes) const
|
||||
{
|
||||
static const string tenand_id_key = "tenantId";
|
||||
static const string policy_key = "policy";
|
||||
static const string settings_key = "settings";
|
||||
static const string tenants_key = "tenants";
|
||||
static const string error_text = "error";
|
||||
|
||||
map<string, string> res;
|
||||
I_UpdateCommunication *update_communication = Singleton::Consume<I_UpdateCommunication>::by<Downloader>();
|
||||
auto downloaded_data = update_communication->downloadAttributeFile(resourse_file);
|
||||
if (!downloaded_data.ok()) return downloaded_data.passErr();
|
||||
|
||||
Document document;
|
||||
document.Parse(downloaded_data.unpack().c_str());
|
||||
if (document.HasParseError()) return genError("JSON file is not valid.");
|
||||
|
||||
const Value &tenants_data = document[tenants_key.c_str()];
|
||||
for (Value::ConstValueIterator itr = tenants_data.Begin(); itr != tenants_data.End(); ++itr) {
|
||||
|
||||
auto tenant_id_obj = itr->FindMember(tenand_id_key.c_str());
|
||||
if (tenant_id_obj == itr->MemberEnd()) continue;
|
||||
|
||||
string tenant_id = tenant_id_obj->value.GetString();
|
||||
|
||||
Value::ConstMemberIterator artifact_data = itr->FindMember(policy_key.c_str());
|
||||
if (artifact_data == itr->MemberEnd()) artifact_data = itr->FindMember(settings_key.c_str());
|
||||
|
||||
if (artifact_data != itr->MemberEnd()) {
|
||||
string file_path = dir_path + "/" + resourse_file.getFileName() + "_" + tenant_id + ".download";
|
||||
|
||||
rapidjson::StringBuffer buffer;
|
||||
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
|
||||
artifact_data->value.Accept(writer);
|
||||
|
||||
I_OrchestrationTools *orchestration_tools = Singleton::Consume<I_OrchestrationTools>::by<Downloader>();
|
||||
if (orchestration_tools->writeFile(buffer.GetString(), file_path)) {
|
||||
res.insert({tenant_id, file_path});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
Value::ConstMemberIterator error_data = itr->FindMember(error_text.c_str());
|
||||
if (error_data != itr->MemberEnd()) {
|
||||
dbgDebug(D_ORCHESTRATOR)
|
||||
<< "Failed to download artifact"
|
||||
<< ", Tenant ID: " << tenant_id
|
||||
<< ", Error message: " << error_data->value.FindMember("message")->value.GetString()
|
||||
<< ", Error ID: " << error_data->value.FindMember("messageId")->value.GetString();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
Maybe<string>
|
||||
Downloader::Impl::downloadFileFromURL(
|
||||
const string &url,
|
||||
const string &checksum,
|
||||
Package::ChecksumTypes checksum_type,
|
||||
const string &service_name) const
|
||||
{
|
||||
dbgDebug(D_ORCHESTRATOR) << "Download file. URL: " << url;
|
||||
|
||||
string new_url = url;
|
||||
bool auth_required = false;
|
||||
auto custom_url = getConfiguration<string>("orchestration", "Custom download url");
|
||||
if (custom_url.ok()) {
|
||||
auto resource_index = url.find_last_of("/");
|
||||
string error_msg = "Failed to parse custom URL. ";
|
||||
if (resource_index == string::npos) {
|
||||
return genError(error_msg + "URL: " + url);
|
||||
}
|
||||
new_url = custom_url.unpack();
|
||||
if (new_url.empty()) {
|
||||
return genError(error_msg + "URL is empty");
|
||||
}
|
||||
if (new_url.back() == '/') {
|
||||
new_url = new_url.substr(0, new_url.size() - 1);
|
||||
}
|
||||
new_url.append(url.substr(resource_index));
|
||||
}
|
||||
// Workaround - only in staging we need to add the auth header
|
||||
static const string jwt_word = "<JWT>";
|
||||
if (new_url.find(jwt_word) != string::npos) {
|
||||
new_url = new_url.substr(jwt_word.length());
|
||||
auth_required = true;
|
||||
}
|
||||
|
||||
URLParser parsed_url(new_url);
|
||||
Maybe<string> base_url = parsed_url.getBaseURL();
|
||||
if (!base_url.ok()) return base_url;
|
||||
Maybe<string> file_path = genError("Empty path");
|
||||
string file_name = service_name + ".download";
|
||||
if (parsed_url.getProtocol() == URLProtocol::LOCAL_FILE) {
|
||||
file_path = getFileFromLocal(base_url.unpack(), file_name);
|
||||
} else {
|
||||
file_path = getFileFromExternalURL(parsed_url, file_name, auth_required);
|
||||
}
|
||||
|
||||
if (!file_path.ok()) {
|
||||
return file_path;
|
||||
}
|
||||
|
||||
auto checksum_validation = validateChecksum(checksum, checksum_type, file_path);
|
||||
if (!checksum_validation.ok()) return checksum_validation;
|
||||
|
||||
I_OrchestrationTools *orchestration_tools = Singleton::Consume<I_OrchestrationTools>::by<Downloader>();
|
||||
if (!orchestration_tools->isNonEmptyFile(file_path.unpack())) {
|
||||
return genError("Failed to download file. URL: " + parsed_url.toString());
|
||||
}
|
||||
|
||||
return file_path;
|
||||
}
|
||||
|
||||
Maybe<string>
|
||||
Downloader::Impl::validateChecksum(
|
||||
const string &checksum,
|
||||
Package::ChecksumTypes checksum_type,
|
||||
Maybe<string> &file_path) const
|
||||
{
|
||||
if (file_path.ok()) {
|
||||
I_OrchestrationTools *orchestration_tools = Singleton::Consume<I_OrchestrationTools>::by<Downloader>();
|
||||
Maybe<string> file_checksum = orchestration_tools->calculateChecksum(checksum_type, file_path.unpack());
|
||||
if (!file_checksum.ok() || checksum != file_checksum.unpack()) {
|
||||
orchestration_tools->removeFile(file_path.unpack());
|
||||
if (!file_checksum.ok()) {
|
||||
return genError("Failed to calculate file checksum, with error: " + file_checksum.getErr());
|
||||
}
|
||||
return genError(
|
||||
"The checksum calculation is not as the expected, " +
|
||||
checksum + " != " + file_checksum.unpack()
|
||||
);
|
||||
}
|
||||
}
|
||||
return file_path;
|
||||
}
|
||||
|
||||
Maybe<string>
|
||||
Downloader::Impl::downloadFileFromFogByHTTP(const GetResourceFile &resourse_file, const string &file_name) const
|
||||
{
|
||||
string file_path = dir_path + "/" + file_name;
|
||||
|
||||
dbgInfo(D_ORCHESTRATOR) << "Downloading file from fog. File: " << resourse_file.getFileName();
|
||||
|
||||
I_UpdateCommunication *update_communication = Singleton::Consume<I_UpdateCommunication>::by<Downloader>();
|
||||
auto downloaded_file = update_communication->downloadAttributeFile(resourse_file);
|
||||
if (!downloaded_file.ok()) return genError(downloaded_file.getErr());
|
||||
dbgInfo(D_ORCHESTRATOR) << "Download completed. File: " << resourse_file.getFileName();
|
||||
|
||||
I_OrchestrationTools *orchestration_tools = Singleton::Consume<I_OrchestrationTools>::by<Downloader>();
|
||||
if (orchestration_tools->writeFile(downloaded_file.unpack(), file_path)) return file_path;
|
||||
return genError("Failed to write the attribute file. File: " + file_name);
|
||||
}
|
||||
|
||||
Maybe<string>
|
||||
Downloader::Impl::getFileFromLocal(const string &local_file_path, const string &file_name) const
|
||||
{
|
||||
string file_path = dir_path + "/" + file_name;
|
||||
I_OrchestrationTools *orchestration_tools = Singleton::Consume<I_OrchestrationTools>::by<Downloader>();
|
||||
if (!orchestration_tools->copyFile(local_file_path, file_path)) {
|
||||
return genError("Get file from local failed. File: " + local_file_path);
|
||||
}
|
||||
|
||||
return file_path;
|
||||
}
|
||||
|
||||
// LCOV_EXCL_START Reason: Depends on real download server.
|
||||
Maybe<string>
|
||||
Downloader::Impl::getFileFromURL(const URLParser &url, const string &file_path, bool auth_required) const
|
||||
{
|
||||
ofstream outFile(file_path, ofstream::out | ofstream::binary);
|
||||
HTTPClient http_client;
|
||||
dbgInfo(D_ORCHESTRATOR) << "Downloading file. URL: " << url;
|
||||
auto get_file_response = http_client.getFile(url, outFile, auth_required);
|
||||
if (!get_file_response.ok()) {
|
||||
Maybe<string> error = genError("Failed to download file from " + url.getBaseURL().unpack() +
|
||||
". Error: " + get_file_response.getErr());
|
||||
dbgWarning(D_ORCHESTRATOR) << "Download failed";
|
||||
return error;
|
||||
}
|
||||
outFile.close();
|
||||
dbgInfo(D_ORCHESTRATOR) << "Download completed. URL: " << url;
|
||||
return file_path;
|
||||
}
|
||||
|
||||
Maybe<string>
|
||||
Downloader::Impl::getFileFromExternalURL(
|
||||
const URLParser &parsed_url,
|
||||
const string &file_name,
|
||||
bool auth_required
|
||||
) const
|
||||
{
|
||||
string file_path = dir_path + "/" + file_name;
|
||||
auto base_url = parsed_url.getBaseURL().unpack();
|
||||
|
||||
string query_path;
|
||||
string query_file;
|
||||
tie(query_path, query_file) = splitQuery(parsed_url.getQuery());
|
||||
|
||||
auto try_dirs = getConfigurationWithDefault<bool>(
|
||||
false,
|
||||
"orchestration",
|
||||
"Add tenant suffix"
|
||||
);
|
||||
if (try_dirs) {
|
||||
vector<string> sub_path;
|
||||
auto agent_details = Singleton::Consume<I_AgentDetails>::by<Downloader>();
|
||||
auto tenant_id = agent_details->getTenantId();
|
||||
if (!tenant_id.empty()) {
|
||||
sub_path.push_back(tenant_id);
|
||||
auto profile_id = agent_details->getProfileId();
|
||||
if (!profile_id.empty()) {
|
||||
sub_path.push_back(profile_id);
|
||||
auto agent_id = agent_details->getAgentId();
|
||||
if(!agent_id.empty()) {
|
||||
sub_path.push_back(agent_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
URLParser currentUrl = parsed_url;
|
||||
while (!sub_path.empty()) {
|
||||
currentUrl.setQuery(query_path + vectorToPath(sub_path) + "/" + query_file);
|
||||
if (getFileFromURL(currentUrl, file_path, auth_required).ok()) return file_path;
|
||||
sub_path.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
return getFileFromURL(parsed_url, file_path, auth_required);
|
||||
}
|
||||
|
||||
tuple<string, string>
|
||||
Downloader::Impl::splitQuery(const string &query) const
|
||||
{
|
||||
size_t index = query.find_last_of("/");
|
||||
if (index == string::npos) return make_tuple(string(), query);
|
||||
return make_tuple(query.substr(0, index), query.substr(index + 1));
|
||||
}
|
||||
|
||||
string
|
||||
Downloader::Impl::vectorToPath(const vector<string> &vec) const
|
||||
{
|
||||
string s;
|
||||
for (const auto &piece : vec) { s += ("/" + piece); }
|
||||
return s;
|
||||
}
|
||||
|
||||
Downloader::Downloader() : Component("Downloader"), pimpl(make_unique<Impl>()) {}
|
||||
|
||||
Downloader::~Downloader() {}
|
||||
|
||||
void
|
||||
Downloader::init()
|
||||
{
|
||||
pimpl->init();
|
||||
}
|
||||
|
||||
void
|
||||
Downloader::preload()
|
||||
{
|
||||
registerExpectedConfiguration<string>("orchestration", "Custom download url");
|
||||
registerExpectedConfiguration<string>("orchestration", "Default file download path");
|
||||
registerExpectedConfiguration<string>("orchestration", "Self signed certificates acceptable");
|
||||
registerExpectedConfiguration<bool>("orchestration", "Add tenant suffix");
|
||||
}
|
||||
|
||||
SASAL_END
|
||||
@@ -0,0 +1,7 @@
|
||||
link_directories(${BOOST_ROOT}/lib)
|
||||
|
||||
add_unit_test(
|
||||
downloader_ut
|
||||
"downloader_ut.cc"
|
||||
"orchestration;orchestration_downloader;orchestration_modules;orchestration_tools;environment;config;update_communication;metric;event_is;-lcurl;-lcrypto;-lssl;-lboost_regex"
|
||||
)
|
||||
431
components/security_apps/orchestration/downloader/downloader_ut/downloader_ut.cc
Executable file
431
components/security_apps/orchestration/downloader/downloader_ut/downloader_ut.cc
Executable file
@@ -0,0 +1,431 @@
|
||||
#include "cptest.h"
|
||||
#include "config.h"
|
||||
#include "config_component.h"
|
||||
#include "downloader.h"
|
||||
#include "enum_range.h"
|
||||
#include "environment.h"
|
||||
#include "mock/mock_mainloop.h"
|
||||
#include "mock/mock_time_get.h"
|
||||
|
||||
#include "mock/mock_update_communication.h"
|
||||
#include "mock/mock_orchestration_tools.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace testing;
|
||||
|
||||
class DownloaderTest : public Test
|
||||
{
|
||||
public:
|
||||
DownloaderTest()
|
||||
{
|
||||
setConfiguration<string>("/tmp", "orchestration", "Default file download path");
|
||||
EXPECT_CALL(mock_orchestration_tools, createDirectory("/tmp")).WillOnce(Return(true));
|
||||
downloader.init();
|
||||
}
|
||||
|
||||
NiceMock<MockMainLoop> mock_mainloop;
|
||||
NiceMock<MockTimeGet> mock_timer;
|
||||
::Environment env;
|
||||
ConfigComponent config_component;
|
||||
StrictMock<MockUpdateCommunication> mock_communication;
|
||||
StrictMock<MockOrchestrationTools> mock_orchestration_tools;
|
||||
Downloader downloader;
|
||||
I_Downloader *i_downloader = Singleton::Consume<I_Downloader>::from(downloader);
|
||||
};
|
||||
|
||||
TEST_F(DownloaderTest, doNothing)
|
||||
{
|
||||
}
|
||||
|
||||
TEST_F(DownloaderTest, downloadFileFromFog)
|
||||
{
|
||||
string fog_response = "bla bla";
|
||||
string checksum = "123";
|
||||
|
||||
GetResourceFile resourse_file(GetResourceFile::ResourceFileType::VIRTUAL_SETTINGS);
|
||||
|
||||
EXPECT_CALL(mock_communication, downloadAttributeFile(resourse_file)).WillOnce(Return(fog_response));
|
||||
|
||||
EXPECT_CALL(
|
||||
mock_orchestration_tools,
|
||||
calculateChecksum(Package::ChecksumTypes::SHA256, "/tmp/virtualSettings.download")
|
||||
).WillOnce(Return(string("123")));
|
||||
|
||||
EXPECT_CALL(mock_orchestration_tools, writeFile(fog_response, "/tmp/virtualSettings.download"))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_orchestration_tools, isNonEmptyFile("/tmp/virtualSettings.download")).WillOnce(Return(true));
|
||||
|
||||
Maybe<string> downloaded_file = i_downloader->downloadFileFromFog(
|
||||
checksum,
|
||||
Package::ChecksumTypes::SHA256,
|
||||
resourse_file
|
||||
);
|
||||
|
||||
EXPECT_THAT(downloaded_file, IsValue("/tmp/virtualSettings.download"));
|
||||
}
|
||||
|
||||
TEST_F(DownloaderTest, downloadFileFromFogFailure)
|
||||
{
|
||||
string checksum = "123";
|
||||
|
||||
Maybe<string> fog_response(genError("Failed to download"));
|
||||
GetResourceFile resourse_file(GetResourceFile::ResourceFileType::SETTINGS);
|
||||
|
||||
EXPECT_CALL(mock_communication, downloadAttributeFile(resourse_file)).WillOnce(Return(fog_response));
|
||||
|
||||
Maybe<string> downloaded_file = i_downloader->downloadFileFromFog(
|
||||
checksum,
|
||||
Package::ChecksumTypes::SHA256,
|
||||
resourse_file
|
||||
);
|
||||
|
||||
EXPECT_FALSE(downloaded_file.ok());
|
||||
EXPECT_THAT(downloaded_file, IsError("Failed to download"));
|
||||
}
|
||||
|
||||
TEST_F(DownloaderTest, registerConfig)
|
||||
{
|
||||
string file_path_value = "/tmp";
|
||||
string signed_certificates_value = "bla";
|
||||
string config_json =
|
||||
"{\n"
|
||||
" \"orchestration\": {\n"
|
||||
" \"Default file download path\": [\n"
|
||||
" {\n"
|
||||
" \"context\": \"All()\",\n"
|
||||
" \"value\": \"" + file_path_value + "\"\n"
|
||||
" }\n"
|
||||
" ],\n"
|
||||
" \"Self signed certificates acceptable\": [\n"
|
||||
" {\n"
|
||||
" \"context\": \"All()\",\n"
|
||||
" \"value\": \"" + signed_certificates_value + "\"\n"
|
||||
" }\n"
|
||||
" ]\n"
|
||||
" }\n"
|
||||
"}\n";
|
||||
|
||||
env.preload();
|
||||
env.init();
|
||||
downloader.preload();
|
||||
istringstream stringstream(config_json);
|
||||
Singleton::Consume<Config::I_Config>::from(config_component)->loadConfiguration(stringstream);
|
||||
|
||||
EXPECT_THAT(
|
||||
getConfiguration<string>("orchestration", "Default file download path"),
|
||||
IsValue(file_path_value)
|
||||
);
|
||||
|
||||
EXPECT_THAT(
|
||||
getConfiguration<string>("orchestration", "Self signed certificates acceptable"),
|
||||
IsValue(signed_certificates_value)
|
||||
);
|
||||
|
||||
env.fini();
|
||||
}
|
||||
|
||||
TEST_F(DownloaderTest, downloadWithBadChecksum)
|
||||
{
|
||||
string local_file_path = "/tmp/test_file.sh";
|
||||
string url = "file://" + local_file_path;
|
||||
string dir_path = getConfigurationWithDefault<string>(
|
||||
"/tmp/orchestration_downloads",
|
||||
"orchestration",
|
||||
"Default file download path"
|
||||
);
|
||||
string service_name = "test";
|
||||
string file_name = service_name + ".download";
|
||||
string file_path = dir_path + "/" + file_name;
|
||||
string checksum = "1234";
|
||||
Package::ChecksumTypes checksum_type = Package::ChecksumTypes::MD5;
|
||||
|
||||
EXPECT_CALL(
|
||||
mock_orchestration_tools,
|
||||
calculateChecksum(checksum_type, file_path)
|
||||
).WillOnce(Return(checksum + "5"));
|
||||
EXPECT_CALL(mock_orchestration_tools, copyFile(local_file_path, file_path)).WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_orchestration_tools, removeFile("/tmp/test.download")).WillOnce(Return(true));
|
||||
EXPECT_THAT(
|
||||
i_downloader->downloadFileFromURL(url, checksum, checksum_type, service_name),
|
||||
IsError("The checksum calculation is not as the expected, 1234 != 12345")
|
||||
);
|
||||
}
|
||||
|
||||
TEST_F(DownloaderTest, downloadFromLocal)
|
||||
{
|
||||
string local_file_path = "/tmp/test_file.sh";
|
||||
string url = "file://" + local_file_path;
|
||||
string dir_path = getConfigurationWithDefault<string>(
|
||||
"/tmp/orchestration_downloads",
|
||||
"orchestration",
|
||||
"Default file download path"
|
||||
);
|
||||
string service_name = "test";
|
||||
string file_name = service_name + ".download";
|
||||
string file_path = dir_path + "/" + file_name;
|
||||
string checksum = "1234";
|
||||
Package::ChecksumTypes checksum_type = Package::ChecksumTypes::MD5;
|
||||
|
||||
EXPECT_CALL(mock_orchestration_tools, calculateChecksum(checksum_type, file_path)).WillOnce(Return(checksum));
|
||||
EXPECT_CALL(mock_orchestration_tools, copyFile(local_file_path, file_path)).WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_orchestration_tools, isNonEmptyFile(file_path)).WillOnce(Return(true));
|
||||
i_downloader->downloadFileFromURL(url, checksum, checksum_type, service_name);
|
||||
}
|
||||
|
||||
TEST_F(DownloaderTest, downloadEmptyFileFromFog)
|
||||
{
|
||||
string fog_response = "bla bla";
|
||||
string checksum = "123";
|
||||
string service_name = "manifest";
|
||||
string empty_str = "";
|
||||
|
||||
GetResourceFile resourse_file(GetResourceFile::ResourceFileType::MANIFEST);
|
||||
|
||||
EXPECT_CALL(mock_communication, downloadAttributeFile(resourse_file)).WillOnce(Return(fog_response));
|
||||
|
||||
EXPECT_CALL(mock_orchestration_tools, writeFile(fog_response, "/tmp/manifest.download"))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_orchestration_tools, isNonEmptyFile("/tmp/manifest.download")).WillOnce(Return(false));
|
||||
|
||||
EXPECT_CALL(
|
||||
mock_orchestration_tools,
|
||||
calculateChecksum(Package::ChecksumTypes::SHA256, "/tmp/manifest.download")
|
||||
).WillOnce(Return(checksum));
|
||||
|
||||
Maybe<string> downloaded_file = i_downloader->downloadFileFromFog(
|
||||
checksum,
|
||||
Package::ChecksumTypes::SHA256,
|
||||
resourse_file
|
||||
);
|
||||
|
||||
EXPECT_FALSE(downloaded_file.ok());
|
||||
EXPECT_THAT(downloaded_file, IsError("Failed to download file manifest"));
|
||||
}
|
||||
|
||||
TEST_F(DownloaderTest, downloadFromCustomURL)
|
||||
{
|
||||
string file_prefix = "file://";
|
||||
string file_name = "/test_file.sh";
|
||||
string local_file_path = "/tmp" + file_name;
|
||||
string url = file_prefix + local_file_path;
|
||||
string custom_URL = "/custom";
|
||||
setConfiguration<string>(
|
||||
string(file_prefix + custom_URL),
|
||||
"orchestration",
|
||||
"Custom download url"
|
||||
);
|
||||
string dir_path = getConfigurationWithDefault<string>(
|
||||
"/tmp/orchestration_downloads",
|
||||
"orchestration",
|
||||
"Default file download path"
|
||||
);
|
||||
string service_name = "test";
|
||||
string download_file_name = service_name + ".download";
|
||||
string download_file_path = dir_path + "/" + download_file_name;
|
||||
string checksum = "1234";
|
||||
Package::ChecksumTypes checksum_type = Package::ChecksumTypes::MD5;
|
||||
EXPECT_CALL(mock_orchestration_tools, copyFile(custom_URL + file_name, download_file_path))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_orchestration_tools, isNonEmptyFile(download_file_path)).WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_orchestration_tools, calculateChecksum(checksum_type, download_file_path))
|
||||
.WillOnce(Return(checksum));
|
||||
|
||||
i_downloader->downloadFileFromURL(url, checksum, checksum_type, service_name);
|
||||
}
|
||||
|
||||
TEST_F(DownloaderTest, CustomURLBackBackslash)
|
||||
{
|
||||
string file_prefix = "file://";
|
||||
string file_name = "test_file.sh";
|
||||
string local_file_path = "/tmp/" + file_name;
|
||||
string url = file_prefix + local_file_path;
|
||||
string custom_URL = "/custom/";
|
||||
setConfiguration<string>(
|
||||
string(file_prefix + custom_URL),
|
||||
"orchestration",
|
||||
"Custom download url"
|
||||
);
|
||||
string dir_path = getConfigurationWithDefault<string>(
|
||||
"/tmp/orchestration_downloads",
|
||||
"orchestration",
|
||||
"Default file download path"
|
||||
);
|
||||
string service_name = "test";
|
||||
string download_file_name = service_name + ".download";
|
||||
string download_file_path = dir_path + "/" + download_file_name;
|
||||
string checksum = "1234";
|
||||
Package::ChecksumTypes checksum_type = Package::ChecksumTypes::MD5;
|
||||
EXPECT_CALL(mock_orchestration_tools, copyFile(custom_URL + file_name, download_file_path))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_orchestration_tools, isNonEmptyFile(download_file_path)).WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_orchestration_tools, calculateChecksum(checksum_type, download_file_path))
|
||||
.WillOnce(Return(checksum));
|
||||
i_downloader->downloadFileFromURL(url, checksum, checksum_type, service_name);
|
||||
}
|
||||
|
||||
TEST_F(DownloaderTest, EmptyCustomURL)
|
||||
{
|
||||
string file_prefix = "file://";
|
||||
string file_name = "/test_file.sh";
|
||||
string local_file_path = "/tmp" + file_name;
|
||||
string url = file_prefix + local_file_path;
|
||||
string custom_URL = "";
|
||||
setConfiguration<string>(
|
||||
string(custom_URL),
|
||||
"orchestration",
|
||||
"Custom download url"
|
||||
);
|
||||
string dir_path = getConfigurationWithDefault<string>(
|
||||
"/tmp/orchestration_downloads",
|
||||
"orchestration",
|
||||
"Default file download path"
|
||||
);
|
||||
string service_name = "test";
|
||||
string download_file_name = service_name + ".download";
|
||||
string download_file_path = dir_path + "/" + download_file_name;
|
||||
string checksum = "1234";
|
||||
Package::ChecksumTypes checksum_type = Package::ChecksumTypes::MD5;
|
||||
EXPECT_THAT(
|
||||
i_downloader->downloadFileFromURL(url, checksum, checksum_type, service_name),
|
||||
IsError("Failed to parse custom URL. URL is empty")
|
||||
);
|
||||
}
|
||||
|
||||
TEST_F(DownloaderTest, download_virtual_policy)
|
||||
{
|
||||
GetResourceFile resourse_file(GetResourceFile::ResourceFileType::VIRTUAL_POLICY);
|
||||
|
||||
resourse_file.addTenant("0000", "1", "checksum0000");
|
||||
resourse_file.addTenant("1111", "2", "checksum1111");
|
||||
|
||||
string tenant_0000_file =
|
||||
"{"
|
||||
"\"waap\":\"108-005\","
|
||||
"\"accessControl\":\"Internal error, check logs\","
|
||||
"\"idk\":\"ed5ac9a6-6924-4ebc-9ace-971896ca33c5\","
|
||||
"\"something\":\"Low\""
|
||||
"}";
|
||||
|
||||
string tenant_1111_file =
|
||||
"{"
|
||||
"\"messageId\":\"108-005\","
|
||||
"\"message\":\"Internal error, check logs\","
|
||||
"\"referenceId\":\"ed5ac9a6-6924-4ebc-9ace-971896ca33c5\","
|
||||
"\"severity\":\"Low\""
|
||||
"}";
|
||||
|
||||
string fog_response =
|
||||
"{\n"
|
||||
" \"tenants\": [\n"
|
||||
" {\n"
|
||||
" \"tenantId\": \"0000\",\n"
|
||||
" \"policy\": {\n"
|
||||
" \"waap\": \"108-005\",\n"
|
||||
" \"accessControl\": \"Internal error, check logs\",\n"
|
||||
" \"idk\": \"ed5ac9a6-6924-4ebc-9ace-971896ca33c5\",\n"
|
||||
" \"something\": \"Low\"\n"
|
||||
" }\n"
|
||||
" },\n"
|
||||
" {\n"
|
||||
" \"tenantId\": \"1111\",\n"
|
||||
" \"policy\": {\n"
|
||||
" \"messageId\": \"108-005\",\n"
|
||||
" \"message\": \"Internal error, check logs\",\n"
|
||||
" \"referenceId\": \"ed5ac9a6-6924-4ebc-9ace-971896ca33c5\",\n"
|
||||
" \"severity\": \"Low\"\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" ]\n"
|
||||
"}";
|
||||
|
||||
EXPECT_CALL(mock_communication, downloadAttributeFile(resourse_file)).WillOnce(Return(fog_response));
|
||||
|
||||
EXPECT_CALL(mock_orchestration_tools, writeFile(tenant_0000_file, "/tmp/virtualPolicy_0000.download"))
|
||||
.WillOnce(Return(true));
|
||||
|
||||
EXPECT_CALL(mock_orchestration_tools, writeFile(tenant_1111_file, "/tmp/virtualPolicy_1111.download"))
|
||||
.WillOnce(Return(true));
|
||||
|
||||
map<string, string> expected_downloaded_files =
|
||||
{
|
||||
{ "0000", "/tmp/virtualPolicy_0000.download" },
|
||||
{ "1111", "/tmp/virtualPolicy_1111.download" }
|
||||
};
|
||||
|
||||
EXPECT_EQ(
|
||||
i_downloader->downloadVirtualFileFromFog(
|
||||
resourse_file,
|
||||
Package::ChecksumTypes::SHA256
|
||||
),
|
||||
expected_downloaded_files
|
||||
);
|
||||
}
|
||||
|
||||
TEST_F(DownloaderTest, download_virtual_settings)
|
||||
{
|
||||
GetResourceFile resourse_file(GetResourceFile::ResourceFileType::VIRTUAL_SETTINGS);
|
||||
|
||||
resourse_file.addTenant("4c721b40-85df-4364-be3d-303a10ee9789", "1", "checksum0000");
|
||||
|
||||
string tenant_0000_file =
|
||||
"{"
|
||||
"\"agentSettings\":["
|
||||
"{"
|
||||
"\"id\":\"f0bd081b-175a-2fb6-c6de-d05d62fdcadf\","
|
||||
"\"key\":\"\","
|
||||
"\"value\":\"\""
|
||||
"}"
|
||||
"],"
|
||||
"\"allowOnlyDefinedApplications\":false,"
|
||||
"\"anyFog\":true,"
|
||||
"\"reverseProxy\":{"
|
||||
"\"assets\":[]"
|
||||
"},"
|
||||
"\"upgradeMode\":\"automatic\""
|
||||
"}";
|
||||
|
||||
string fog_response =
|
||||
"{\n"
|
||||
" \"tenants\": [\n"
|
||||
" {\n"
|
||||
" \"tenantId\": \"4c721b40-85df-4364-be3d-303a10ee9789\",\n"
|
||||
" \"settings\": {\n"
|
||||
" \"agentSettings\": [\n"
|
||||
" {\n"
|
||||
" \"id\": \"f0bd081b-175a-2fb6-c6de-d05d62fdcadf\",\n"
|
||||
" \"key\": \"\",\n"
|
||||
" \"value\": \"\"\n"
|
||||
" }\n"
|
||||
" ],\n"
|
||||
" \"allowOnlyDefinedApplications\": false,\n"
|
||||
" \"anyFog\": true,\n"
|
||||
" \"reverseProxy\": {\n"
|
||||
" \"assets\": []\n"
|
||||
" },\n"
|
||||
" \"upgradeMode\": \"automatic\"\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" ]\n"
|
||||
"}";
|
||||
|
||||
EXPECT_CALL(mock_communication, downloadAttributeFile(resourse_file)).WillOnce(Return(fog_response));
|
||||
|
||||
EXPECT_CALL(
|
||||
mock_orchestration_tools,
|
||||
writeFile(tenant_0000_file, "/tmp/virtualSettings_4c721b40-85df-4364-be3d-303a10ee9789.download")
|
||||
).WillOnce(Return(true));
|
||||
|
||||
map<string, string> expected_downloaded_files = {
|
||||
{ "4c721b40-85df-4364-be3d-303a10ee9789",
|
||||
"/tmp/virtualSettings_4c721b40-85df-4364-be3d-303a10ee9789.download"
|
||||
}
|
||||
};
|
||||
|
||||
EXPECT_EQ(
|
||||
i_downloader->downloadVirtualFileFromFog(
|
||||
resourse_file,
|
||||
Package::ChecksumTypes::SHA256
|
||||
),
|
||||
expected_downloaded_files
|
||||
);
|
||||
}
|
||||
276
components/security_apps/orchestration/downloader/http_client.cc
Executable file
276
components/security_apps/orchestration/downloader/http_client.cc
Executable file
@@ -0,0 +1,276 @@
|
||||
// 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 "http_client.h"
|
||||
|
||||
#include "curl_client.h"
|
||||
#include "downloader.h"
|
||||
#include "debug.h"
|
||||
#include "i_encryptor.h"
|
||||
#include "url_parser.h"
|
||||
#include "sasal.h"
|
||||
#include "config.h"
|
||||
#include "i_environment.h"
|
||||
#include "orchestration_comp.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
|
||||
using boost::asio::ip::tcp;
|
||||
using namespace std;
|
||||
|
||||
SASAL_START // Orchestration - Communication
|
||||
|
||||
USE_DEBUG_FLAG(D_ORCHESTRATOR);
|
||||
USE_DEBUG_FLAG(D_HTTP_REQUEST);
|
||||
|
||||
// LCOV_EXCL_START Reason: Depends on real download server.
|
||||
class ClientConnection
|
||||
{
|
||||
public:
|
||||
ClientConnection(
|
||||
const URLParser &_url,
|
||||
const Maybe<string> &_proxy_url,
|
||||
const Maybe<uint16_t> &_proxy_port,
|
||||
const Maybe<string> &_proxy_auth,
|
||||
const string &_token)
|
||||
:
|
||||
url(_url),
|
||||
proxy_url(_proxy_url),
|
||||
proxy_port(_proxy_port),
|
||||
proxy_auth(_proxy_auth),
|
||||
token(_token)
|
||||
{
|
||||
}
|
||||
|
||||
Maybe<void>
|
||||
handleConnect()
|
||||
{
|
||||
if (!url.getBaseURL().ok()) {
|
||||
return genError("Failed to handle connection. Error: " + url.getBaseURL().getErr());
|
||||
}
|
||||
string server_name = url.getBaseURL().unpack();
|
||||
string port = url.getPort();
|
||||
string query = url.getQuery();
|
||||
string host = server_name;
|
||||
try {
|
||||
if (stoi(port) != 80) {
|
||||
host = host + ":" + port;
|
||||
}
|
||||
} catch (const exception &err) {
|
||||
return genError("Failed to parse port to a number. Port: " + port );
|
||||
}
|
||||
|
||||
chrono::duration<unsigned int, ratio<1>> sleep_time(60);
|
||||
io_stream.expires_from_now(sleep_time);
|
||||
|
||||
if (proxy_url.ok()) {
|
||||
if (!proxy_port.ok()) {
|
||||
return genError(
|
||||
"Failed to handle connection to server. proxy domain is defined with invalid port, Error: " +
|
||||
proxy_port.getErr()
|
||||
);
|
||||
}
|
||||
io_stream.connect(proxy_url.unpack(), to_string(proxy_port.unpack()));
|
||||
} else {
|
||||
io_stream.connect(server_name, port);
|
||||
}
|
||||
|
||||
if (!io_stream) {
|
||||
return genError("Failed to handle connection to server. Error: " + io_stream.error().message());
|
||||
}
|
||||
|
||||
string request_url = query;
|
||||
if (proxy_url.ok()) {
|
||||
request_url = host + query;
|
||||
}
|
||||
|
||||
stringstream http_request;
|
||||
http_request << "GET http://" << request_url << " HTTP/1.1\r\n";
|
||||
http_request << "Host: " << host << "\r\n";
|
||||
if (!token.empty()) {
|
||||
http_request << "Authorization: " << "Bearer " << token << "\r\n";
|
||||
}
|
||||
http_request << "User-Agent: Infinity Next (a7030abf93a4c13)\r\n";
|
||||
|
||||
auto i_trace_env = Singleton::Consume<I_Environment>::by<OrchestrationComp>();
|
||||
http_request << i_trace_env->getCurrentHeaders();
|
||||
http_request << "Accept: */*\r\n";
|
||||
|
||||
if (proxy_url.ok()) {
|
||||
http_request << "Accept-Encoding: identity";
|
||||
http_request << "Connection: close\r\n";
|
||||
http_request << "Proxy-Connection: Keep-Alive\r\n";
|
||||
|
||||
if (proxy_auth.ok()) {
|
||||
I_Encryptor *encryptor = Singleton::Consume<I_Encryptor>::by<Downloader>();
|
||||
http_request << "Proxy-Authorization: Basic " + encryptor->base64Encode(proxy_auth.unpack()) + "\r\n";
|
||||
}
|
||||
http_request << "\r\n";
|
||||
} else {
|
||||
http_request << "Connection: close\r\n\r\n";
|
||||
}
|
||||
|
||||
dbgTrace(D_HTTP_REQUEST) << "Sending the following HTTP Request: " << endl << http_request.str();
|
||||
io_stream << http_request.str();
|
||||
return Maybe<void>();
|
||||
}
|
||||
|
||||
Maybe<void>
|
||||
handleResponse(ofstream &out_file)
|
||||
{
|
||||
string response_http_version;
|
||||
io_stream >> response_http_version;
|
||||
unsigned int status_code;
|
||||
io_stream >> status_code;
|
||||
string status_message;
|
||||
getline(io_stream, status_message);
|
||||
|
||||
if (!io_stream || response_http_version.substr(0, 5) != "HTTP/") {
|
||||
return genError("Invalid response");
|
||||
}
|
||||
|
||||
if (status_code != 200) {
|
||||
return genError("HTTP response returned with status code " + status_code);
|
||||
}
|
||||
|
||||
string header;
|
||||
vector<string> headers;
|
||||
while (getline(io_stream, header) && header != "\r") {
|
||||
headers.push_back(header);
|
||||
}
|
||||
|
||||
out_file << io_stream.rdbuf();
|
||||
|
||||
dbgTrace(D_HTTP_REQUEST)
|
||||
<< "Received HTTP Response with the following data (downloaded file will not be printed):"
|
||||
<< endl
|
||||
<< response_http_version
|
||||
<< " "
|
||||
<< status_code
|
||||
<< " "
|
||||
<< status_message
|
||||
<< endl
|
||||
<< makeSeparatedStr(headers, "\n");
|
||||
|
||||
|
||||
return Maybe<void>();
|
||||
}
|
||||
|
||||
private:
|
||||
const URLParser url;
|
||||
const Maybe<string> proxy_url;
|
||||
const Maybe<uint16_t> proxy_port;
|
||||
const Maybe<string> proxy_auth;
|
||||
const string &token;
|
||||
boost::asio::ip::tcp::iostream io_stream;
|
||||
};
|
||||
|
||||
Maybe<void>
|
||||
HTTPClient::getFile(const URLParser &url, ofstream &out_file, bool auth_required)
|
||||
{
|
||||
auto message = Singleton::Consume<I_Messaging>::by<HTTPClient>();
|
||||
auto load_env_proxy = message->loadProxy();
|
||||
if (!load_env_proxy.ok()) return load_env_proxy;
|
||||
|
||||
string token = "";
|
||||
if (auth_required) {
|
||||
auto message = Singleton::Consume<I_Messaging>::by<HTTPClient>();
|
||||
token = message->getAccessToken();
|
||||
}
|
||||
|
||||
if (url.isOverSSL()) {
|
||||
auto get_file_over_ssl_res = getFileSSL(url, out_file, token);
|
||||
if (!get_file_over_ssl_res.ok())
|
||||
{
|
||||
//CURL fallback
|
||||
dbgWarning(D_ORCHESTRATOR) << "Failed to get file over SSL. Trying via CURL (SSL).";
|
||||
return curlGetFileOverSSL(url, out_file, token);
|
||||
}
|
||||
return get_file_over_ssl_res;
|
||||
}
|
||||
auto get_file_http_res = getFileHttp(url, out_file, token);
|
||||
if (!get_file_http_res.ok())
|
||||
{
|
||||
//CURL fallback
|
||||
dbgWarning(D_ORCHESTRATOR) << "Failed to get file over HTTP. Trying via CURL (HTTP).";
|
||||
return curlGetFileOverHttp(url, out_file, token);
|
||||
}
|
||||
|
||||
return get_file_http_res;
|
||||
}
|
||||
|
||||
Maybe<void>
|
||||
HTTPClient::curlGetFileOverHttp(const URLParser &url, ofstream &out_file, const string &token)
|
||||
{
|
||||
try {
|
||||
auto message = Singleton::Consume<I_Messaging>::by<HTTPClient>();
|
||||
|
||||
HttpCurl http_curl_client(
|
||||
url,
|
||||
out_file,
|
||||
token,
|
||||
message->getProxyDomain(ProxyProtocol::HTTPS),
|
||||
message->getProxyPort(ProxyProtocol::HTTPS),
|
||||
message->getProxyCredentials(ProxyProtocol::HTTPS));
|
||||
|
||||
http_curl_client.setCurlOpts();
|
||||
bool connection_ok = http_curl_client.connect();
|
||||
if (!connection_ok)
|
||||
{
|
||||
stringstream url_s;
|
||||
url_s << url;
|
||||
string err_msg = string("Failed to get file over HTTP. URL: ") + url_s.str();
|
||||
return genError(err_msg);
|
||||
}
|
||||
|
||||
// As this class is a temporal solution catch all exception types is enabled.
|
||||
} catch (const exception &e) {
|
||||
string err_msg = "Failed to get file over HTTP. Exception: " + string(e.what());
|
||||
return genError(err_msg);
|
||||
}
|
||||
|
||||
return Maybe<void>();
|
||||
}
|
||||
|
||||
Maybe<void>
|
||||
HTTPClient::getFileHttp(const URLParser &url, ofstream &out_file, const string &token)
|
||||
{
|
||||
try {
|
||||
auto message = Singleton::Consume<I_Messaging>::by<HTTPClient>();
|
||||
ClientConnection client_connection(
|
||||
url,
|
||||
message->getProxyDomain(ProxyProtocol::HTTP),
|
||||
message->getProxyPort(ProxyProtocol::HTTP),
|
||||
message->getProxyCredentials(ProxyProtocol::HTTP),
|
||||
token
|
||||
);
|
||||
auto handle_connect_res = client_connection.handleConnect();
|
||||
if (!handle_connect_res.ok()) return handle_connect_res;
|
||||
|
||||
return client_connection.handleResponse(out_file);
|
||||
|
||||
// As this class is a temporal solution catch all exception types is enabled.
|
||||
} catch (const exception &e) {
|
||||
string err_msg = "Failed to get file over HTTP. Exception: " + string(e.what());
|
||||
return genError(err_msg);
|
||||
}
|
||||
|
||||
return Maybe<void>();
|
||||
}
|
||||
// LCOV_EXCL_STOP
|
||||
|
||||
SASAL_END
|
||||
39
components/security_apps/orchestration/downloader/http_client.h
Executable file
39
components/security_apps/orchestration/downloader/http_client.h
Executable file
@@ -0,0 +1,39 @@
|
||||
// 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 __HTTP_CLIENT_H__
|
||||
#define __HTTP_CLIENT_H__
|
||||
|
||||
#include <string>
|
||||
#include "maybe_res.h"
|
||||
#include "url_parser.h"
|
||||
#include "i_messaging.h"
|
||||
|
||||
// LCOV_EXCL_START Reason: Depends on real download server.
|
||||
class HTTPClient : public Singleton::Consume<I_Messaging>
|
||||
{
|
||||
public:
|
||||
HTTPClient() = default;
|
||||
|
||||
Maybe<void> getFile(const URLParser &url, std::ofstream &out_file, bool auth_required);
|
||||
|
||||
private:
|
||||
std::string loadCAChainDir();
|
||||
Maybe<void> getFileSSL(const URLParser &url, std::ofstream &out_file, const std::string &_token);
|
||||
Maybe<void> getFileHttp(const URLParser &url, std::ofstream &out_file, const std::string &_token);
|
||||
Maybe<void> curlGetFileOverSSL(const URLParser &url, std::ofstream &out_file, const std::string &_token);
|
||||
Maybe<void> curlGetFileOverHttp(const URLParser &url, std::ofstream &out_file, const std::string &_token);
|
||||
};
|
||||
// LCOV_EXCL_STOP
|
||||
|
||||
#endif // __HTTP_CLIENT_H__
|
||||
619
components/security_apps/orchestration/downloader/https_client.cc
Executable file
619
components/security_apps/orchestration/downloader/https_client.cc
Executable file
@@ -0,0 +1,619 @@
|
||||
// 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 "http_client.h"
|
||||
#include "curl_client.h"
|
||||
|
||||
#include "debug.h"
|
||||
#include "i_agent_details.h"
|
||||
#include "i_encryptor.h"
|
||||
#include "downloader.h"
|
||||
#include "config.h"
|
||||
#include "sasal.h"
|
||||
#include "boost/uuid/uuid.hpp"
|
||||
#include "boost/uuid/uuid_generators.hpp"
|
||||
#include <boost/asio/deadline_timer.hpp>
|
||||
#include "boost/uuid/uuid_io.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <istream>
|
||||
#include <ostream>
|
||||
#include <fstream>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/bind/bind.hpp>
|
||||
#include <boost/asio/ssl.hpp>
|
||||
#include <exception>
|
||||
|
||||
using namespace boost::placeholders;
|
||||
using boost::asio::ip::tcp;
|
||||
using namespace std;
|
||||
|
||||
SASAL_START // Orchestration - Communication
|
||||
|
||||
USE_DEBUG_FLAG(D_COMMUNICATION);
|
||||
USE_DEBUG_FLAG(D_HTTP_REQUEST);
|
||||
USE_DEBUG_FLAG(D_ORCHESTRATOR);
|
||||
|
||||
// LCOV_EXCL_START Reason: Depends on real download server.
|
||||
class BadResponseFromServer : public exception
|
||||
{
|
||||
public:
|
||||
BadResponseFromServer() : message("Bad response returned from server") {}
|
||||
BadResponseFromServer(const string &msg) : message(msg) {}
|
||||
const char *
|
||||
what() const throw()
|
||||
{
|
||||
return message.c_str();
|
||||
}
|
||||
|
||||
private:
|
||||
string message;
|
||||
};
|
||||
|
||||
class Client
|
||||
{
|
||||
public:
|
||||
Client(
|
||||
ofstream &out_file,
|
||||
boost::asio::io_service &io_service,
|
||||
boost::asio::ssl::context &context,
|
||||
const URLParser &_url,
|
||||
const Maybe<string> &_proxy_url,
|
||||
const Maybe<uint16_t> &_proxy_port,
|
||||
const Maybe<string> &_proxy_auth,
|
||||
const string &_token)
|
||||
:
|
||||
out_file(out_file),
|
||||
url(_url),
|
||||
proxy_url(_proxy_url),
|
||||
proxy_port(_proxy_port),
|
||||
proxy_auth(_proxy_auth),
|
||||
resolver_(io_service),
|
||||
deadline(io_service),
|
||||
socket_(io_service),
|
||||
ssl_socket(socket_, context),
|
||||
token(_token)
|
||||
{
|
||||
}
|
||||
|
||||
Maybe<void>
|
||||
handleConnection()
|
||||
{
|
||||
ostream request_stream(&request_);
|
||||
stringstream http_request;
|
||||
http_request << "GET " << url.getQuery() << " HTTP/1.1\r\n";
|
||||
string host = url.getBaseURL().unpack();
|
||||
string port = url.getPort();
|
||||
int port_int;
|
||||
try {
|
||||
port_int = stoi(port);
|
||||
} catch (const exception &err) {
|
||||
dbgWarning(D_COMMUNICATION)
|
||||
<< "Failed to convert port number from string. Port: "
|
||||
<< port
|
||||
<< ", Error: "
|
||||
<< err.what();
|
||||
return genError("Failed to parse port to a number. Port: " + port);
|
||||
}
|
||||
if (port_int != 443) {
|
||||
host = host + ":" + port;
|
||||
}
|
||||
|
||||
http_request << "Host: " << host << "\r\n";
|
||||
|
||||
if (!token.empty()) {
|
||||
http_request << "Authorization: " << "Bearer " << token << "\r\n";
|
||||
}
|
||||
http_request << "User-Agent: Infinity Next (a7030abf93a4c13)\r\n";
|
||||
boost::uuids::uuid correlation_id;
|
||||
try {
|
||||
correlation_id = uuid_random_gen();
|
||||
} catch (const boost::uuids::entropy_error &) {
|
||||
dbgWarning(D_COMMUNICATION) << "Failed to generate random correlation id - entropy exception";
|
||||
}
|
||||
http_request << "X-Trace-Id: " + boost::uuids::to_string(correlation_id) + "\r\n";
|
||||
http_request << "Accept: */*\r\n";
|
||||
http_request << "Connection: close\r\n\r\n";
|
||||
|
||||
request_stream << http_request.str();
|
||||
|
||||
deadline.expires_from_now(boost::posix_time::minutes(5));
|
||||
deadline.async_wait(boost::bind(&Client::checkDeadline, this, _1));
|
||||
|
||||
if (proxy_url.ok()) {
|
||||
if (!proxy_port.ok()) {
|
||||
dbgWarning(D_COMMUNICATION)
|
||||
<< "Failed to connect to proxy due to invalid port value, Error: "
|
||||
<< proxy_port.getErr();
|
||||
|
||||
return genError(
|
||||
"Failed to handle connection to server. proxy port is invalid, Error: " +
|
||||
proxy_port.getErr()
|
||||
);
|
||||
}
|
||||
if (port_int == 443) host = host + ":" + port;
|
||||
ostream connect_request_stream(&connect_request);
|
||||
stringstream proxy_request;
|
||||
proxy_request << "CONNECT " << host << " HTTP/1.1\r\n";
|
||||
proxy_request << "Host: " << host << "\r\n";
|
||||
if (proxy_auth.ok()) {
|
||||
I_Encryptor *encryptor = Singleton::Consume<I_Encryptor>::by<Downloader>();
|
||||
proxy_request
|
||||
<< "Proxy-Authorization: Basic "
|
||||
<< encryptor->base64Encode(proxy_auth.unpack())
|
||||
<< "\r\n";
|
||||
}
|
||||
proxy_request << "\r\n";
|
||||
|
||||
dbgTrace(D_HTTP_REQUEST) << "Connecting to proxy: " << endl << proxy_request.str();
|
||||
connect_request_stream << proxy_request.str();
|
||||
|
||||
tcp::resolver::query query(proxy_url.unpack(), to_string(proxy_port.unpack()));
|
||||
resolver_.async_resolve(
|
||||
query,
|
||||
boost::bind(
|
||||
&Client::overProxyResolver,
|
||||
this,
|
||||
boost::asio::placeholders::error,
|
||||
boost::asio::placeholders::iterator
|
||||
)
|
||||
);
|
||||
} else {
|
||||
tcp::resolver::query query(url.getBaseURL().unpack(), port);
|
||||
resolver_.async_resolve(
|
||||
query,
|
||||
boost::bind(
|
||||
&Client::handleResolve,
|
||||
this,
|
||||
boost::asio::placeholders::error,
|
||||
boost::asio::placeholders::iterator
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
dbgTrace(D_HTTP_REQUEST) << "Sending the following HTTP Request: " << endl << http_request.str();
|
||||
return Maybe<void>();
|
||||
}
|
||||
|
||||
private:
|
||||
void
|
||||
checkDeadline(const boost::system::error_code &err)
|
||||
{
|
||||
if (err) return;
|
||||
if (deadline.expires_at() <= boost::asio::deadline_timer::traits_type::now()) {
|
||||
boost::system::error_code ignored_ec = boost::asio::error::operation_aborted;
|
||||
socket_.close(ignored_ec);
|
||||
deadline.expires_at(boost::posix_time::pos_infin);
|
||||
return;
|
||||
}
|
||||
deadline.async_wait(boost::bind(&Client::checkDeadline, this, _1));
|
||||
}
|
||||
|
||||
void
|
||||
overProxyResolver(const boost::system::error_code &err, tcp::resolver::iterator endpoint_iterator)
|
||||
{
|
||||
if (!err) {
|
||||
boost::asio::async_connect(socket_, endpoint_iterator,
|
||||
boost::bind(&Client::overProxyHandleConnect, this,
|
||||
boost::asio::placeholders::error));
|
||||
} else {
|
||||
string err_msg = "Failed to connect to proxy. Error: " + err.message();
|
||||
throw BadResponseFromServer(err_msg);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
overProxyHandleConnect(const boost::system::error_code &err)
|
||||
{
|
||||
if (!err) {
|
||||
boost::asio::async_write(socket_, connect_request,
|
||||
boost::bind(&Client::overProxyHandleWriteRequest, this,
|
||||
boost::asio::placeholders::error));
|
||||
} else {
|
||||
string err_msg = "Failed to connect to proxy. Error: " + err.message();
|
||||
throw BadResponseFromServer(err_msg);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
overProxyHandleWriteRequest(const boost::system::error_code &err)
|
||||
{
|
||||
if (!err) {
|
||||
boost::asio::async_read_until(
|
||||
socket_,
|
||||
response_,
|
||||
"\r\n",
|
||||
boost::bind(&Client::overProxyHandleReadStatusLine, this, boost::asio::placeholders::error)
|
||||
);
|
||||
} else {
|
||||
string err_msg = "Failed to write over proxy. Error: " + err.message();
|
||||
throw BadResponseFromServer(err_msg);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
overProxyHandleReadStatusLine(const boost::system::error_code &err)
|
||||
{
|
||||
if (err) {
|
||||
string err_msg = "Failed to read status line over proxy. Error: " + err.message();
|
||||
throw BadResponseFromServer(err_msg);
|
||||
}
|
||||
// Check that response is OK.
|
||||
istream response_stream(&response_);
|
||||
string response_http_version;
|
||||
response_stream >> response_http_version;
|
||||
unsigned int status_code;
|
||||
response_stream >> status_code;
|
||||
string status_message;
|
||||
getline(response_stream, status_message);
|
||||
if (!response_stream || response_http_version.substr(0, 5) != "HTTP/") {
|
||||
throw BadResponseFromServer("Invalid response");
|
||||
return;
|
||||
}
|
||||
|
||||
if (status_code != 200) {
|
||||
string err_msg = "Response returned with status code " + status_code;
|
||||
throw BadResponseFromServer(err_msg);
|
||||
}
|
||||
|
||||
dbgTrace(D_HTTP_REQUEST)
|
||||
<< "Received HTTP Response over proxied connection with the following data:"
|
||||
<< endl
|
||||
<< response_http_version
|
||||
<< " "
|
||||
<< status_code
|
||||
<< " "
|
||||
<< status_message;
|
||||
|
||||
if (getProfileAgentSettingWithDefault<bool>(false, "agent.config.message.ignoreSslValidation") == false) {
|
||||
ssl_socket.set_verify_mode(boost::asio::ssl::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert);
|
||||
ssl_socket.set_verify_callback(boost::bind(&Client::verifyCertificate, this, _1, _2));
|
||||
} else {
|
||||
dbgWarning(D_HTTP_REQUEST) << "Ignoring SSL validation";
|
||||
}
|
||||
|
||||
ssl_socket.async_handshake(
|
||||
boost::asio::ssl::stream_base::client,
|
||||
boost::bind(&Client::handleHandshake, this, boost::asio::placeholders::error)
|
||||
);
|
||||
}
|
||||
|
||||
void
|
||||
handleResolve(const boost::system::error_code &err, tcp::resolver::iterator endpoint_iterator)
|
||||
{
|
||||
if (!err) {
|
||||
boost::asio::async_connect(ssl_socket.lowest_layer(), endpoint_iterator,
|
||||
boost::bind(&Client::handleConnect, this,
|
||||
boost::asio::placeholders::error));
|
||||
} else {
|
||||
string message = "Failed to connect. Error: " + err.message();
|
||||
throw BadResponseFromServer(message);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
verifyCertificate(bool preverified, boost::asio::ssl::verify_context &ctx)
|
||||
{
|
||||
if (!token.empty()) {
|
||||
X509_STORE_CTX *cts = ctx.native_handle();
|
||||
|
||||
switch (X509_STORE_CTX_get_error(cts))
|
||||
{
|
||||
case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
|
||||
dbgWarning(D_ORCHESTRATOR) << "SSL verification error: X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT";
|
||||
break;
|
||||
case X509_V_ERR_CERT_NOT_YET_VALID:
|
||||
case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
|
||||
dbgWarning(D_ORCHESTRATOR) << "SSL verification error: Certificate not yet valid";
|
||||
break;
|
||||
case X509_V_ERR_CERT_HAS_EXPIRED:
|
||||
case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
|
||||
dbgWarning(D_ORCHESTRATOR) << "Certificate expired";
|
||||
break;
|
||||
case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
|
||||
case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
|
||||
dbgDebug(D_ORCHESTRATOR) << "Self signed certificate in chain";
|
||||
if (getConfigurationWithDefault(false, "orchestration", "Self signed certificates acceptable")) {
|
||||
preverified = true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (!preverified) {
|
||||
dbgWarning(D_ORCHESTRATOR)
|
||||
<< "Certificate verification error number: "
|
||||
<< X509_STORE_CTX_get_error(cts);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return preverified;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
handleConnect(const boost::system::error_code &err)
|
||||
{
|
||||
if (!err) {
|
||||
if (getProfileAgentSettingWithDefault<bool>(false, "agent.config.message.ignoreSslValidation") == false) {
|
||||
ssl_socket.set_verify_mode(
|
||||
boost::asio::ssl::verify_peer |
|
||||
boost::asio::ssl::verify_fail_if_no_peer_cert
|
||||
);
|
||||
ssl_socket.set_verify_callback(boost::bind(&Client::verifyCertificate, this, _1, _2));
|
||||
} else {
|
||||
dbgWarning(D_HTTP_REQUEST) << "Ignoring SSL validation";
|
||||
}
|
||||
|
||||
ssl_socket.async_handshake(boost::asio::ssl::stream_base::client,
|
||||
boost::bind(&Client::handleHandshake, this,
|
||||
boost::asio::placeholders::error));
|
||||
} else {
|
||||
string err_message = "Failed to connect. Error: " + err.message();
|
||||
throw BadResponseFromServer(err_message);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
handleHandshake(const boost::system::error_code &error)
|
||||
{
|
||||
if (!error) {
|
||||
boost::asio::buffer_cast<const char*>(request_.data());
|
||||
|
||||
boost::asio::async_write(ssl_socket, request_,
|
||||
boost::bind(&Client::handleWriteRequest, this,
|
||||
boost::asio::placeholders::error));
|
||||
} else {
|
||||
string err_message = "Handshake failed. Error: " + error.message();
|
||||
throw BadResponseFromServer(err_message);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
handleWriteRequest(const boost::system::error_code &err)
|
||||
{
|
||||
if (!err) {
|
||||
boost::asio::async_read_until(ssl_socket, resp, "\r\n",
|
||||
boost::bind(&Client::handleReadStatusLine, this,
|
||||
boost::asio::placeholders::error));
|
||||
} else {
|
||||
string err_message = "Failed to handle write request. Error: " + err.message();
|
||||
throw BadResponseFromServer(err_message);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
handleReadStatusLine(const boost::system::error_code &err)
|
||||
{
|
||||
if (!err) {
|
||||
istream response_stream(&resp);
|
||||
string http_version;
|
||||
response_stream >> http_version;
|
||||
unsigned int status_code;
|
||||
response_stream >> status_code;
|
||||
string status_message;
|
||||
getline(response_stream, status_message);
|
||||
dbgTrace(D_HTTP_REQUEST)
|
||||
<< "Received HTTP Response with the following data:"
|
||||
<< endl
|
||||
<< http_version
|
||||
<< " "
|
||||
<< status_code;
|
||||
|
||||
if (!response_stream || http_version.substr(0, 5) != "HTTP/") {
|
||||
string err_message = "Invalid response";
|
||||
throw BadResponseFromServer(err_message);
|
||||
}
|
||||
if (status_code != 200) {
|
||||
string err_message = "HTTPS response returned with status code " + to_string(status_code)
|
||||
+ ". URL: " + url.toString();
|
||||
throw BadResponseFromServer(err_message);
|
||||
}
|
||||
|
||||
boost::asio::async_read_until(ssl_socket, resp, "\r\n\r\n",
|
||||
boost::bind(&Client::handleReadHeaders, this,
|
||||
boost::asio::placeholders::error));
|
||||
} else {
|
||||
dbgWarning(D_COMMUNICATION) << "Failed to read response status. Error:" << err.message();
|
||||
string err_message = "Failed to read status. Error: " + err.message();
|
||||
throw BadResponseFromServer(err_message);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
handleReadHeaders(const boost::system::error_code &err)
|
||||
{
|
||||
if (!err) {
|
||||
// Process the response headers.
|
||||
istream response_stream(&resp);
|
||||
string header;
|
||||
vector<string> headers;
|
||||
while (getline(response_stream, header) && header != "\r") {
|
||||
headers.push_back(header);
|
||||
}
|
||||
|
||||
dbgTrace(D_HTTP_REQUEST) << "Received Response headers:" << endl << makeSeparatedStr(headers, "\n");
|
||||
// Write whatever content we already have to output.
|
||||
if (resp.size() > 0)
|
||||
out_file << &resp;
|
||||
|
||||
// Start reading remaining data until EOF.
|
||||
boost::asio::async_read(ssl_socket, resp,
|
||||
boost::asio::transfer_at_least(1),
|
||||
boost::bind(&Client::handleReadContent, this,
|
||||
boost::asio::placeholders::error));
|
||||
} else {
|
||||
dbgWarning(D_COMMUNICATION) << "Failed to read response headers. Error:" << err.message();
|
||||
string err_message = "Failed to read headers. Error: " + err.message();
|
||||
throw BadResponseFromServer(err_message);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
handleReadContent(const boost::system::error_code &err)
|
||||
{
|
||||
if (!err) {
|
||||
// Write all of the data that has been read so far.
|
||||
out_file << &resp;
|
||||
// Continue reading remaining data until EOF.
|
||||
boost::asio::async_read(
|
||||
ssl_socket,
|
||||
resp,
|
||||
boost::asio::transfer_at_least(1),
|
||||
boost::bind(&Client::handleReadContent, this, boost::asio::placeholders::error)
|
||||
);
|
||||
} else if (err != boost::asio::error::eof && err != boost::asio::ssl::error::stream_truncated) {
|
||||
dbgWarning(D_COMMUNICATION) << "Failed to read response body. Error:" << err.message();
|
||||
string err_message = "Failed to read content. Error: " + err.message();
|
||||
throw BadResponseFromServer(err_message);
|
||||
} else if (err == boost::asio::ssl::error::stream_truncated) {
|
||||
dbgError(D_COMMUNICATION) << "Had SSL warning during reading response body stage. Error:" << err.message();
|
||||
deadline.cancel();
|
||||
} else {
|
||||
deadline.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
ofstream &out_file;
|
||||
const URLParser &url;
|
||||
const Maybe<string> proxy_url;
|
||||
const Maybe<uint16_t> proxy_port;
|
||||
const Maybe<string> proxy_auth;
|
||||
tcp::resolver resolver_;
|
||||
boost::asio::deadline_timer deadline;
|
||||
boost::asio::ip::tcp::socket socket_;
|
||||
boost::asio::ssl::stream<boost::asio::ip::tcp::socket&> ssl_socket;
|
||||
boost::asio::streambuf request_;
|
||||
boost::asio::streambuf connect_request;
|
||||
boost::asio::streambuf response_;
|
||||
boost::asio::streambuf resp;
|
||||
const string &token;
|
||||
boost::uuids::random_generator uuid_random_gen;
|
||||
};
|
||||
|
||||
string
|
||||
HTTPClient::loadCAChainDir()
|
||||
{
|
||||
string ca_chain_dir;
|
||||
auto agent_details = Singleton::Consume<I_AgentDetails>::by<Downloader>();
|
||||
auto load_ca_chain_dir = agent_details->getOpenSSLDir();
|
||||
if (load_ca_chain_dir.ok()) {
|
||||
ca_chain_dir = load_ca_chain_dir.unpack();
|
||||
}
|
||||
return getConfigurationWithDefault<string>(ca_chain_dir, "message", "Certificate authority directory");
|
||||
}
|
||||
|
||||
Maybe<void>
|
||||
HTTPClient::getFileSSL(const URLParser &url, ofstream &out_file, const string &token)
|
||||
{
|
||||
try {
|
||||
boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23);
|
||||
if (!token.empty()) {
|
||||
string cert_file_path = getConfigurationWithDefault<string>(
|
||||
getFilesystemPathConfig() + "/certs/fog.pem",
|
||||
"message",
|
||||
"Certificate chain file path"
|
||||
);
|
||||
dbgTrace(D_ORCHESTRATOR) << "Http client, cert file path: " << cert_file_path;
|
||||
auto trusted_ca_directory = getConfiguration<string>("message", "Trusted CA directory");
|
||||
if (trusted_ca_directory.ok() && !trusted_ca_directory.unpack().empty()) {
|
||||
ctx.add_verify_path(trusted_ca_directory.unpack());
|
||||
} else {
|
||||
string cert_file_path = getConfigurationWithDefault<string>(
|
||||
getFilesystemPathConfig() + "/certs/fog.pem",
|
||||
"message",
|
||||
"Certificate chain file path"
|
||||
);
|
||||
ctx.load_verify_file(cert_file_path);
|
||||
}
|
||||
}
|
||||
boost::asio::io_service io_service;
|
||||
auto message = Singleton::Consume<I_Messaging>::by<HTTPClient>();
|
||||
|
||||
Client client(
|
||||
out_file,
|
||||
io_service,
|
||||
ctx,
|
||||
url,
|
||||
message->getProxyDomain(ProxyProtocol::HTTPS),
|
||||
message->getProxyPort(ProxyProtocol::HTTPS),
|
||||
message->getProxyCredentials(ProxyProtocol::HTTPS),
|
||||
token
|
||||
);
|
||||
|
||||
auto connection_result = client.handleConnection();
|
||||
if (!connection_result.ok()) {
|
||||
return connection_result;
|
||||
};
|
||||
|
||||
auto mainloop = Singleton::Consume<I_MainLoop>::by<Downloader>();
|
||||
while (!io_service.stopped()) {
|
||||
io_service.poll_one();
|
||||
mainloop->yield(true);
|
||||
}
|
||||
} catch (const exception &e) {
|
||||
dbgWarning(D_COMMUNICATION) << "Failed to get file over HTTPS. Error:" << string(e.what());
|
||||
string error_str = "Failed to get file over HTTPS, exception: " + string(e.what());
|
||||
return genError(error_str);
|
||||
}
|
||||
|
||||
return Maybe<void>();
|
||||
}
|
||||
|
||||
Maybe<void>
|
||||
HTTPClient::curlGetFileOverSSL(const URLParser &url, ofstream &out_file, const string &token)
|
||||
{
|
||||
try {
|
||||
string cert_file_path;
|
||||
if (!token.empty())
|
||||
{
|
||||
cert_file_path = getConfigurationWithDefault<string>(
|
||||
getFilesystemPathConfig() + "/certs/fog.pem",
|
||||
"message",
|
||||
"Certificate chain file path"
|
||||
);
|
||||
}
|
||||
|
||||
auto message = Singleton::Consume<I_Messaging>::by<HTTPClient>();
|
||||
|
||||
HttpsCurl ssl_curl_client(
|
||||
url,
|
||||
out_file,
|
||||
token,
|
||||
message->getProxyDomain(ProxyProtocol::HTTPS),
|
||||
message->getProxyPort(ProxyProtocol::HTTPS),
|
||||
message->getProxyCredentials(ProxyProtocol::HTTPS),
|
||||
cert_file_path);
|
||||
|
||||
ssl_curl_client.setCurlOpts();
|
||||
bool connection_ok = ssl_curl_client.connect();
|
||||
if (!connection_ok)
|
||||
{
|
||||
stringstream url_s;
|
||||
url_s << url;
|
||||
string err_msg = string("Failed to get file over HTTPS. URL: ") + url_s.str();
|
||||
return genError(err_msg);
|
||||
}
|
||||
|
||||
} catch (const exception &e) {
|
||||
dbgWarning(D_COMMUNICATION) << "Failed to get file over HTTPS. Error:" << string(e.what());
|
||||
string error_str = "Failed to get file over HTTPS, exception: " + string(e.what());
|
||||
return genError(error_str);
|
||||
}
|
||||
|
||||
return Maybe<void>();
|
||||
}
|
||||
|
||||
// LCOV_EXCL_STOP
|
||||
|
||||
SASAL_END
|
||||
3
components/security_apps/orchestration/health_check/CMakeLists.txt
Executable file
3
components/security_apps/orchestration/health_check/CMakeLists.txt
Executable file
@@ -0,0 +1,3 @@
|
||||
add_library(health_check health_check.cc)
|
||||
|
||||
add_subdirectory(health_check_ut)
|
||||
340
components/security_apps/orchestration/health_check/health_check.cc
Executable file
340
components/security_apps/orchestration/health_check/health_check.cc
Executable file
@@ -0,0 +1,340 @@
|
||||
// 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 "health_checker.h"
|
||||
|
||||
#include <string>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "config.h"
|
||||
#include "log_generator.h"
|
||||
#include "health_check_manager.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace ReportIS;
|
||||
|
||||
USE_DEBUG_FLAG(D_HEALTH_CHECK);
|
||||
|
||||
class HealthChecker::Impl
|
||||
{
|
||||
public:
|
||||
void
|
||||
init()
|
||||
{
|
||||
i_mainloop = Singleton::Consume<I_MainLoop>::by<HealthChecker>();
|
||||
i_socket = Singleton::Consume<I_Socket>::by<HealthChecker>();
|
||||
initConfig();
|
||||
initServerSocket();
|
||||
|
||||
registerConfigLoadCb(
|
||||
[&]()
|
||||
{
|
||||
initConfig();
|
||||
initServerSocket();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void
|
||||
initServerSocket()
|
||||
{
|
||||
if (!enable) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!checkInternalHealthCheckStatus()) {
|
||||
reportError("Internal health check failed. Wait for restart.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (port == 0) {
|
||||
string error_msg =
|
||||
"Cannot initialize health check component, listening port was not provided. "
|
||||
"Please provide valid port (>0).";
|
||||
reportError(error_msg);
|
||||
return;
|
||||
}
|
||||
|
||||
if (server_sock == -1) {
|
||||
i_mainloop->addOneTimeRoutine(
|
||||
I_MainLoop::RoutineType::System,
|
||||
[this] () { HandleProbeStartup(); },
|
||||
"Health check probe listener startup",
|
||||
false
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
fini()
|
||||
{
|
||||
closeConnection();
|
||||
}
|
||||
|
||||
private:
|
||||
bool
|
||||
checkInternalHealthCheckStatus()
|
||||
{
|
||||
dbgTrace(D_HEALTH_CHECK) << "Start agent general health check.";
|
||||
|
||||
HealthCheckStatus status =
|
||||
Singleton::Consume<I_Health_Check_Manager>::by<HealthChecker>()->getAggregatedStatus();
|
||||
|
||||
dbgTrace(D_HEALTH_CHECK)
|
||||
<< "Finished agent general health check. Received aggregated status: "
|
||||
<< HealthCheckStatusReply::convertHealthCheckStatusToStr(status);
|
||||
|
||||
return status != HealthCheckStatus::UNHEALTHY;
|
||||
}
|
||||
|
||||
void
|
||||
reportError(const string &error_msg)
|
||||
{
|
||||
dbgWarning(D_HEALTH_CHECK) << error_msg;
|
||||
LogGen(
|
||||
error_msg,
|
||||
Audience::SECURITY,
|
||||
Severity::CRITICAL,
|
||||
Priority::URGENT,
|
||||
Tags::ORCHESTRATOR
|
||||
);
|
||||
}
|
||||
|
||||
void
|
||||
closeConnection()
|
||||
{
|
||||
dbgDebug(D_HEALTH_CHECK) << "Closing connection";
|
||||
if (server_sock > 0) {
|
||||
i_socket->closeSocket(server_sock);
|
||||
server_sock = -1;
|
||||
dbgDebug(D_HEALTH_CHECK) << "Server socket closed";
|
||||
}
|
||||
|
||||
if (routine_id > 0 && i_mainloop->doesRoutineExist(routine_id)) {
|
||||
i_mainloop->stop(routine_id);
|
||||
routine_id = 0;
|
||||
}
|
||||
|
||||
for (auto socket_routine : client_sockets_routines) {
|
||||
auto routine = socket_routine.first;
|
||||
if (routine > 0 && i_mainloop->doesRoutineExist(routine)) {
|
||||
i_mainloop->stop(routine);
|
||||
}
|
||||
auto socket = socket_routine.second;
|
||||
|
||||
if (socket > 0) {
|
||||
i_socket->closeSocket(socket);
|
||||
}
|
||||
}
|
||||
client_sockets_routines.clear();
|
||||
}
|
||||
|
||||
void
|
||||
initCloudVendorConfig()
|
||||
{
|
||||
static const map<string, pair<string, int>> ip_port_defaults_map = {
|
||||
{"Azure", make_pair("168.63.129.16", 8117)},
|
||||
{"Aws", make_pair("", 8117)}
|
||||
};
|
||||
auto cloud_vendor_maybe = getSetting<string>("reverseProxy", "cloudVendorName");
|
||||
if (cloud_vendor_maybe.ok()) {
|
||||
const string cloud_vendor = cloud_vendor_maybe.unpack();
|
||||
auto value = ip_port_defaults_map.find(cloud_vendor);
|
||||
if (value != ip_port_defaults_map.end()) {
|
||||
const pair<string, uint> &ip_port_pair = value->second;
|
||||
ip_address = ip_port_pair.first;
|
||||
port = ip_port_pair.second;
|
||||
enable = true;
|
||||
}
|
||||
}
|
||||
|
||||
ip_address = getProfileAgentSettingWithDefault<string>(
|
||||
ip_address,
|
||||
"agent.config.orchestration.healthCheckProbe.IP"
|
||||
);
|
||||
port = getProfileAgentSettingWithDefault<uint>(port, "agent.config.orchestration.healthCheckProbe.port");
|
||||
enable = getProfileAgentSettingWithDefault<bool>(enable, "agent.config.orchestration.healthCheckProbe.enable");
|
||||
|
||||
ip_address = getConfigurationWithDefault<string>(ip_address, "Health Check", "Probe IP");
|
||||
port = getConfigurationWithDefault<uint>(port, "Health Check", "Probe port");
|
||||
enable = getConfigurationWithDefault<bool>(enable, "Health Check", "Probe enabled");
|
||||
}
|
||||
|
||||
void
|
||||
initConfig()
|
||||
{
|
||||
auto prev_ip_address = ip_address;
|
||||
auto prev_port = port;
|
||||
|
||||
initCloudVendorConfig();
|
||||
|
||||
max_connections = getProfileAgentSettingWithDefault<uint>(
|
||||
10,
|
||||
"agent.config.orchestration.healthCheckProbe.maximunConnections"
|
||||
);
|
||||
max_connections = getConfigurationWithDefault<uint>(
|
||||
max_connections,
|
||||
"Health Check",
|
||||
"Probe maximun open connections"
|
||||
);
|
||||
|
||||
max_retry_interval = getProfileAgentSettingWithDefault<uint>(
|
||||
600,
|
||||
"agent.config.orchestration.healthCheckProbe.socketReopenPeriod"
|
||||
);
|
||||
max_retry_interval = getConfigurationWithDefault<uint>(
|
||||
max_retry_interval,
|
||||
"Health Check",
|
||||
"Probe socket reopen period"
|
||||
);
|
||||
if (!enable) {
|
||||
if (server_sock != -1) closeConnection();
|
||||
return;
|
||||
}
|
||||
|
||||
if (prev_ip_address != ip_address || prev_port != port) {
|
||||
if (server_sock != -1) closeConnection();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
HandleProbeStartup()
|
||||
{
|
||||
size_t next_retry_interval = 1;
|
||||
while (server_sock == -1) {
|
||||
next_retry_interval =
|
||||
next_retry_interval < max_retry_interval ? next_retry_interval*2 : max_retry_interval;
|
||||
auto socket = i_socket->genSocket(
|
||||
I_Socket::SocketType::TCP,
|
||||
false,
|
||||
true,
|
||||
"0.0.0.0:" + to_string(port)
|
||||
);
|
||||
if (socket.ok()) {
|
||||
dbgInfo(D_HEALTH_CHECK) << "Successfully created probe listener."
|
||||
<< " port: "
|
||||
<< port;
|
||||
server_sock = socket.unpack();
|
||||
} else {
|
||||
dbgWarning(D_HEALTH_CHECK)
|
||||
<< "Failed to set up socket:"
|
||||
<< ", Error: "
|
||||
<< socket.getErr()
|
||||
<< ", trying again to set up socket in "
|
||||
<< next_retry_interval
|
||||
<< " seconds";
|
||||
i_mainloop->yield(chrono::seconds(next_retry_interval));
|
||||
}
|
||||
}
|
||||
routine_id = i_mainloop->addFileRoutine(
|
||||
I_MainLoop::RoutineType::RealTime,
|
||||
server_sock,
|
||||
[this] () { handleConnection(); },
|
||||
"Health check probe server",
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
void
|
||||
handleConnection()
|
||||
{
|
||||
if (open_connections_counter >= max_connections) {
|
||||
dbgDebug(D_HEALTH_CHECK)
|
||||
<< "Cannot serve new client, reached maximun open connections bound which is:"
|
||||
<< open_connections_counter
|
||||
<< "maximun allowed: "
|
||||
<< max_connections;
|
||||
return;
|
||||
}
|
||||
Maybe<I_Socket::socketFd> accepted_socket = i_socket->acceptSocket(server_sock, false, ip_address);
|
||||
if (!accepted_socket.ok()) {
|
||||
dbgWarning(D_HEALTH_CHECK)
|
||||
<< "Failed to accept a new client socket: "
|
||||
<< accepted_socket.getErr();
|
||||
return;
|
||||
}
|
||||
|
||||
auto new_client_socket = accepted_socket.unpack();
|
||||
if (new_client_socket <= 0) {
|
||||
i_socket->closeSocket(new_client_socket);
|
||||
dbgWarning(D_HEALTH_CHECK)
|
||||
<< "Failed to initialize communication, generated client socket is OK yet negative";
|
||||
return;
|
||||
}
|
||||
|
||||
dbgDebug(D_HEALTH_CHECK) << "Successfully accepted client, client fd: " << new_client_socket;
|
||||
open_connections_counter++;
|
||||
auto curr_routine = i_mainloop->addFileRoutine(
|
||||
I_MainLoop::RoutineType::RealTime,
|
||||
new_client_socket,
|
||||
[this] ()
|
||||
{
|
||||
auto curr_routine_id = i_mainloop->getCurrentRoutineId().unpack();
|
||||
auto curr_client_socket = client_sockets_routines[curr_routine_id];
|
||||
auto data_recieved = i_socket->receiveData(curr_client_socket, sizeof(uint8_t), false);
|
||||
if (!data_recieved.ok()) {
|
||||
dbgDebug(D_HEALTH_CHECK) << "Connection with client closed, client fd: " << curr_client_socket;
|
||||
open_connections_counter--;
|
||||
i_socket->closeSocket(curr_client_socket);
|
||||
client_sockets_routines.erase(curr_routine_id);
|
||||
i_mainloop->stop();
|
||||
}
|
||||
},
|
||||
"Health check probe connection handler",
|
||||
true
|
||||
);
|
||||
client_sockets_routines[curr_routine] = new_client_socket;
|
||||
}
|
||||
|
||||
bool enable;
|
||||
uint max_retry_interval;
|
||||
unordered_map<I_MainLoop::RoutineID, I_Socket::socketFd> client_sockets_routines;
|
||||
bool is_first_run = true;
|
||||
uint open_connections_counter = 0;
|
||||
uint max_connections = 0;
|
||||
string ip_address = "";
|
||||
uint port = 0;
|
||||
I_Socket::socketFd server_sock = -1;
|
||||
I_MainLoop::RoutineID routine_id = 0;
|
||||
I_MainLoop *i_mainloop = nullptr;
|
||||
I_Socket *i_socket = nullptr;
|
||||
I_Health_Check_Manager *i_health_check_manager = nullptr;
|
||||
};
|
||||
|
||||
HealthChecker::HealthChecker() : Component("HealthChecker"), pimpl(make_unique<Impl>()) {}
|
||||
HealthChecker::~HealthChecker() {}
|
||||
|
||||
void
|
||||
HealthChecker::preload()
|
||||
{
|
||||
registerExpectedConfiguration<uint>("Health Check", "Probe maximun open connections");
|
||||
registerExpectedConfiguration<bool>("Health Check", "Probe enabled");
|
||||
registerExpectedConfiguration<string>("Health Check", "Probe IP");
|
||||
registerExpectedConfiguration<uint>("Health Check", "Probe port");
|
||||
registerExpectedConfiguration<uint>("Health Check", "Probe socket reopen period");
|
||||
registerExpectedSetting<string>("reverseProxy", "cloudVendorName");
|
||||
}
|
||||
|
||||
void
|
||||
HealthChecker::init()
|
||||
{
|
||||
pimpl->init();
|
||||
}
|
||||
|
||||
void
|
||||
HealthChecker::fini()
|
||||
{
|
||||
pimpl->fini();
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
link_directories(${BOOST_ROOT}/lib)
|
||||
|
||||
add_unit_test(
|
||||
health_check_ut
|
||||
"health_check_ut.cc"
|
||||
"health_check;mainloop;singleton;agent_details;config;logging;metric;event_is;health_check_manager;-lboost_regex"
|
||||
)
|
||||
@@ -0,0 +1,260 @@
|
||||
#include "health_checker.h"
|
||||
|
||||
#include "cptest.h"
|
||||
#include "agent_details.h"
|
||||
#include "mock/mock_logging.h"
|
||||
#include "mock/mock_time_get.h"
|
||||
#include "mock/mock_socket_is.h"
|
||||
#include "mock/mock_mainloop.h"
|
||||
#include "health_check_manager.h"
|
||||
|
||||
#include "config.h"
|
||||
#include "config_component.h"
|
||||
#include "singleton.h"
|
||||
#include "environment.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace testing;
|
||||
|
||||
USE_DEBUG_FLAG(D_HEALTH_CHECK);
|
||||
|
||||
class HealthCheckerTest : public testing::Test
|
||||
{
|
||||
public:
|
||||
HealthCheckerTest()
|
||||
{
|
||||
setConfiguration(true, "Health Check", "Probe enabled");
|
||||
i_health_check_manager = Singleton::Consume<I_Health_Check_Manager>::from(health_check_manager);
|
||||
Debug::setUnitTestFlag(D_HEALTH_CHECK, Debug::DebugLevel::TRACE);
|
||||
Debug::setNewDefaultStdout(&capture_debug);
|
||||
}
|
||||
|
||||
~HealthCheckerTest()
|
||||
{
|
||||
Debug::setNewDefaultStdout(&cout);
|
||||
|
||||
if (server_socket > 0) {
|
||||
EXPECT_THAT(capture_debug.str(), HasSubstr("Server socket closed"));
|
||||
EXPECT_CALL(mock_socket, closeSocket(server_socket));
|
||||
}
|
||||
health_checker.fini();
|
||||
}
|
||||
|
||||
ostringstream capture_debug;
|
||||
StrictMock<MockMainLoop> mock_mainloop;
|
||||
NiceMock<MockTimeGet> mock_time_get;
|
||||
::Environment env;
|
||||
NiceMock<MockLogging> mock_log;
|
||||
AgentDetails agent_details;
|
||||
StrictMock<MockSocketIS> mock_socket;
|
||||
I_Socket::socketFd server_socket = -1;
|
||||
Context ctx;
|
||||
ConfigComponent config;
|
||||
HealthChecker health_checker;
|
||||
I_MainLoop::Routine connection_handler_routine;
|
||||
I_MainLoop::Routine client_connection_handler_routine;
|
||||
I_MainLoop::Routine handle_probe_routine;
|
||||
//StrictMock<MockHealthCheckManager> mock_health_check_manager;
|
||||
HealthCheckManager health_check_manager;
|
||||
I_Health_Check_Manager *i_health_check_manager;
|
||||
};
|
||||
|
||||
TEST_F(HealthCheckerTest, empty)
|
||||
{
|
||||
}
|
||||
|
||||
TEST_F(HealthCheckerTest, load_policy)
|
||||
{
|
||||
health_checker.preload();
|
||||
health_checker.init();
|
||||
|
||||
stringstream config;
|
||||
config << "{}";
|
||||
EXPECT_TRUE(Singleton::Consume<Config::I_Config>::from<ConfigComponent>()->loadConfiguration(config));
|
||||
}
|
||||
|
||||
TEST_F(HealthCheckerTest, clientConnection)
|
||||
{
|
||||
string ip = "1.2.3.4";
|
||||
setConfiguration(ip, "Health Check", "Probe IP");
|
||||
uint port = 11600;
|
||||
setConfiguration(port, "Health Check", "Probe port");
|
||||
|
||||
EXPECT_CALL(
|
||||
mock_mainloop,
|
||||
addOneTimeRoutine(I_MainLoop::RoutineType::System, _, _, false)
|
||||
).WillOnce(DoAll(SaveArg<1>(&handle_probe_routine), Return(0)));
|
||||
|
||||
EXPECT_CALL(
|
||||
mock_socket,
|
||||
genSocket(I_Socket::SocketType::TCP, false, true, _)
|
||||
).WillRepeatedly(Return(1));
|
||||
|
||||
EXPECT_CALL(
|
||||
mock_mainloop,
|
||||
addFileRoutine(I_MainLoop::RoutineType::RealTime, _, _, _, true)
|
||||
).WillRepeatedly(DoAll(SaveArg<2>(&connection_handler_routine), Return(0)));
|
||||
|
||||
int socket = 1;
|
||||
EXPECT_CALL(mock_socket, acceptSocket(1, false, ip)).WillOnce(Return(socket));
|
||||
EXPECT_CALL(mock_mainloop, getCurrentRoutineId()).WillRepeatedly(Return(0));
|
||||
EXPECT_CALL(mock_socket, receiveData(_, 1, false)).WillOnce(Return(vector<char>()));
|
||||
EXPECT_CALL(mock_socket, closeSocket(socket)).Times(2);
|
||||
health_checker.init();
|
||||
handle_probe_routine();
|
||||
connection_handler_routine();
|
||||
connection_handler_routine();
|
||||
health_checker.fini();
|
||||
}
|
||||
|
||||
TEST_F(HealthCheckerTest, loadFromDynamicConfiguration)
|
||||
{
|
||||
uint port = 11600;
|
||||
|
||||
EXPECT_CALL(
|
||||
mock_socket,
|
||||
genSocket(I_Socket::SocketType::TCP, false, true, _)
|
||||
).WillRepeatedly(Return(1));
|
||||
|
||||
EXPECT_CALL(
|
||||
mock_mainloop,
|
||||
addFileRoutine(I_MainLoop::RoutineType::RealTime, _, _, _, true)
|
||||
).WillRepeatedly(DoAll(SaveArg<2>(&connection_handler_routine), Return(0)));
|
||||
|
||||
health_checker.init();
|
||||
health_checker.preload();
|
||||
EXPECT_THAT(
|
||||
capture_debug.str(),
|
||||
HasSubstr(
|
||||
"Cannot initialize health check component, "
|
||||
"listening port was not provided. Please provide valid port (>0)."
|
||||
)
|
||||
);
|
||||
|
||||
setConfiguration(string("1.2.3.4"), "Health Check", "Probe IP");
|
||||
setConfiguration(port, "Health Check", "Probe port");
|
||||
}
|
||||
|
||||
TEST_F(HealthCheckerTest, connectionsLimit)
|
||||
{
|
||||
string ip = "1.2.3.4";
|
||||
setConfiguration(ip, "Health Check", "Probe IP");
|
||||
uint port = 11600;
|
||||
setConfiguration(port, "Health Check", "Probe port");
|
||||
uint a = 0;
|
||||
setConfiguration(a, "Health Check", "Probe maximun open connections");
|
||||
|
||||
EXPECT_CALL(
|
||||
mock_mainloop,
|
||||
addOneTimeRoutine(I_MainLoop::RoutineType::System, _, _, false)
|
||||
).WillOnce(DoAll(SaveArg<1>(&handle_probe_routine), Return(0)));
|
||||
|
||||
EXPECT_CALL(
|
||||
mock_socket,
|
||||
genSocket(I_Socket::SocketType::TCP, false, true, _)
|
||||
).WillRepeatedly(Return(1));
|
||||
|
||||
EXPECT_CALL(
|
||||
mock_mainloop,
|
||||
addFileRoutine(I_MainLoop::RoutineType::RealTime, _, _, _, true)
|
||||
).WillRepeatedly(DoAll(SaveArg<2>(&connection_handler_routine), Return(0)));
|
||||
|
||||
EXPECT_CALL(mock_mainloop, doesRoutineExist(_)).WillRepeatedly(Return(false));
|
||||
EXPECT_CALL(mock_socket, acceptSocket(1, false, ip)).WillRepeatedly(Return(1));
|
||||
EXPECT_CALL(mock_socket, receiveData(_, 1, false)).WillRepeatedly(Return(vector<char>()));
|
||||
EXPECT_CALL(mock_socket, closeSocket(_)).WillRepeatedly(Return());
|
||||
health_checker.init();
|
||||
handle_probe_routine();
|
||||
connection_handler_routine();
|
||||
|
||||
EXPECT_THAT(
|
||||
capture_debug.str(), HasSubstr("Cannot serve new client, reached maximun open connections")
|
||||
);
|
||||
}
|
||||
|
||||
TEST_F(HealthCheckerTest, disablingAfterEnabled)
|
||||
{
|
||||
string ip = "1.2.3.4";
|
||||
setConfiguration(ip, "Health Check", "Probe IP");
|
||||
uint port = 11600;
|
||||
setConfiguration(port, "Health Check", "Probe port");
|
||||
|
||||
EXPECT_CALL(
|
||||
mock_mainloop,
|
||||
addOneTimeRoutine(I_MainLoop::RoutineType::System, _, _, false)
|
||||
).WillOnce(DoAll(SaveArg<1>(&handle_probe_routine), Return(0)));
|
||||
|
||||
EXPECT_CALL(
|
||||
mock_socket,
|
||||
genSocket(I_Socket::SocketType::TCP, false, true, _)
|
||||
).WillRepeatedly(Return(1));
|
||||
|
||||
EXPECT_CALL(
|
||||
mock_mainloop,
|
||||
addFileRoutine(I_MainLoop::RoutineType::RealTime, _, _, _, true)
|
||||
).WillRepeatedly(DoAll(SaveArg<2>(&connection_handler_routine), Return(0)));
|
||||
|
||||
int socket = 1;
|
||||
EXPECT_CALL(mock_socket, acceptSocket(1, false, ip)).WillOnce(Return(socket));
|
||||
EXPECT_CALL(mock_mainloop, getCurrentRoutineId()).WillRepeatedly(Return(0));
|
||||
EXPECT_CALL(mock_socket, receiveData(_, 1, false)).WillOnce(Return(vector<char>()));
|
||||
EXPECT_CALL(mock_socket, closeSocket(socket)).Times(2);
|
||||
health_checker.init();
|
||||
handle_probe_routine();
|
||||
connection_handler_routine();
|
||||
connection_handler_routine();
|
||||
setConfiguration(false, "Health Check", "Probe enabled");
|
||||
}
|
||||
|
||||
TEST_F(HealthCheckerTest, noPort)
|
||||
{
|
||||
health_checker.init();
|
||||
health_checker.preload();
|
||||
|
||||
EXPECT_THAT(
|
||||
capture_debug.str(),
|
||||
HasSubstr(
|
||||
"Cannot initialize health check component, "
|
||||
"listening port was not provided. Please provide valid port (>0)."
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
TEST_F(HealthCheckerTest, changePortIpConfig)
|
||||
{
|
||||
string ip = "1.2.3.4";
|
||||
setConfiguration(ip, "Health Check", "Probe IP");
|
||||
uint port = 11600;
|
||||
setConfiguration(port, "Health Check", "Probe port");
|
||||
|
||||
EXPECT_CALL(
|
||||
mock_mainloop,
|
||||
addOneTimeRoutine(I_MainLoop::RoutineType::System, _, _, false)
|
||||
).WillOnce(DoAll(SaveArg<1>(&handle_probe_routine), Return(0)));
|
||||
|
||||
EXPECT_CALL(
|
||||
mock_socket,
|
||||
genSocket(I_Socket::SocketType::TCP, false, true, _)
|
||||
).WillRepeatedly(Return(1));
|
||||
|
||||
EXPECT_CALL(
|
||||
mock_mainloop,
|
||||
addFileRoutine(I_MainLoop::RoutineType::RealTime, _, _, _, true)
|
||||
).WillRepeatedly(DoAll(SaveArg<2>(&connection_handler_routine), Return(0)));
|
||||
|
||||
int socket = 1;
|
||||
EXPECT_CALL(mock_socket, acceptSocket(1, false, ip)).WillOnce(Return(socket));
|
||||
EXPECT_CALL(mock_mainloop, getCurrentRoutineId()).WillRepeatedly(Return(0));
|
||||
EXPECT_CALL(mock_socket, receiveData(_, 1, false)).Times(2).WillRepeatedly(Return(vector<char>()));
|
||||
EXPECT_CALL(mock_socket, closeSocket(socket)).Times(2);
|
||||
health_checker.init();
|
||||
handle_probe_routine();
|
||||
connection_handler_routine();
|
||||
connection_handler_routine();
|
||||
setConfiguration(false, "Health Check", "Probe enabled");
|
||||
string new_ip = "1.1.1.1";
|
||||
setConfiguration(new_ip, "Health Check", "Probe IP");
|
||||
uint new_port = 11111;
|
||||
setConfiguration(new_port, "Health Check", "Probe port");
|
||||
connection_handler_routine();
|
||||
}
|
||||
59
components/security_apps/orchestration/hybrid_mode_telemetry.cc
Executable file
59
components/security_apps/orchestration/hybrid_mode_telemetry.cc
Executable file
@@ -0,0 +1,59 @@
|
||||
// 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_mode_telemetry.h"
|
||||
#include "debug.h"
|
||||
#include "orchestration_comp.h"
|
||||
#include "i_shell_cmd.h"
|
||||
#include <algorithm>
|
||||
|
||||
using namespace std;
|
||||
|
||||
USE_DEBUG_FLAG(D_ORCHESTRATOR);
|
||||
|
||||
static inline string &
|
||||
trim(string &in)
|
||||
{
|
||||
in.erase(in.begin(), find_if(in.begin(), in.end(), not1(ptr_fun<int, int>(isspace))));
|
||||
in.erase(find_if(in.rbegin(), in.rend(), not1(ptr_fun<int, int>(isspace))).base(), in.end());
|
||||
return in;
|
||||
}
|
||||
|
||||
void
|
||||
HybridModeMetric::upon(const HybridModeMetricEvent &)
|
||||
{
|
||||
auto shell_cmd = Singleton::Consume<I_ShellCmd>::by<OrchestrationComp>();
|
||||
auto maybe_cmd_output = shell_cmd->getExecOutput(
|
||||
getFilesystemPathConfig() + "/watchdog/cp-nano-watchdog --restart_count"
|
||||
);
|
||||
|
||||
// get wd process restart count
|
||||
if (!maybe_cmd_output.ok()) {
|
||||
dbgWarning(D_ORCHESTRATOR)
|
||||
<< "Watchdog was unable to provide the process restart count. Error: "
|
||||
<< maybe_cmd_output.getErr();
|
||||
return;
|
||||
}
|
||||
string cmd_output = maybe_cmd_output.unpack();
|
||||
trim(cmd_output);
|
||||
dbgDebug(D_ORCHESTRATOR) << "Watchdog process counter: " << cmd_output;
|
||||
|
||||
try {
|
||||
wd_process_restart.report(stoi(cmd_output));
|
||||
dbgDebug(D_ORCHESTRATOR) << "Succesfully reported Watchdog process counter: " << cmd_output;
|
||||
} catch (invalid_argument &) {
|
||||
dbgWarning(D_ORCHESTRATOR) << "counter value is not a number: " << cmd_output;
|
||||
} catch (...) {
|
||||
dbgWarning(D_ORCHESTRATOR) << "Reporting counter value failed with unexpected error";
|
||||
}
|
||||
}
|
||||
301
components/security_apps/orchestration/include/fog_authenticator.h
Executable file
301
components/security_apps/orchestration/include/fog_authenticator.h
Executable file
@@ -0,0 +1,301 @@
|
||||
// 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 __FOG_AUTHENTICATOR_H__
|
||||
#define __FOG_AUTHENTICATOR_H__
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include "cereal/archives/json.hpp"
|
||||
|
||||
#include "i_update_communication.h"
|
||||
#include "i_orchestration_tools.h"
|
||||
#include "i_agent_details.h"
|
||||
#include "i_orchestration_status.h"
|
||||
#include "i_messaging.h"
|
||||
#include "i_mainloop.h"
|
||||
#include "i_encryptor.h"
|
||||
#include "i_details_resolver.h"
|
||||
#include "i_rest_api.h"
|
||||
#include "i_time_get.h"
|
||||
#include "i_encryptor.h"
|
||||
#include "maybe_res.h"
|
||||
|
||||
class FogAuthenticator
|
||||
:
|
||||
public I_UpdateCommunication,
|
||||
Singleton::Consume<I_RestApi>,
|
||||
Singleton::Consume<I_AgentDetails>,
|
||||
Singleton::Consume<I_DetailsResolver>,
|
||||
Singleton::Consume<I_OrchestrationStatus>,
|
||||
Singleton::Consume<I_OrchestrationTools>,
|
||||
Singleton::Consume<I_Encryptor>,
|
||||
Singleton::Consume<I_MainLoop>,
|
||||
Singleton::Consume<I_Messaging>,
|
||||
Singleton::Consume<I_TimeGet>
|
||||
{
|
||||
class AccessToken
|
||||
{
|
||||
public:
|
||||
AccessToken(const std::string &token, std::chrono::seconds expiration);
|
||||
|
||||
std::chrono::seconds getRemainingTime() const;
|
||||
|
||||
const std::string & getToken() const { return token; }
|
||||
uint getExpiration() const { return expiration.count(); }
|
||||
|
||||
private:
|
||||
std::string token;
|
||||
std::chrono::seconds expiration;
|
||||
std::chrono::microseconds received_time;
|
||||
};
|
||||
|
||||
class AccessTokenProvider : public ServerRest
|
||||
{
|
||||
public:
|
||||
void doCall() override;
|
||||
static std::function<Maybe<AccessToken>()> getAccessToken;
|
||||
|
||||
private:
|
||||
S2C_PARAM(std::string, token);
|
||||
S2C_PARAM(uint, expiration);
|
||||
};
|
||||
|
||||
public:
|
||||
class RegistrationData
|
||||
{
|
||||
enum class AuthenticationType { Token, PresharedSecret, COUNT };
|
||||
|
||||
public:
|
||||
RegistrationData() = default;
|
||||
RegistrationData(const RegistrationData &) = default;
|
||||
RegistrationData(const std::string &_env_token);
|
||||
|
||||
void serialize(cereal::JSONOutputArchive &out_ar) const;
|
||||
void serialize(cereal::JSONInputArchive &in_ar);
|
||||
|
||||
private:
|
||||
AuthenticationType type;
|
||||
std::string data;
|
||||
};
|
||||
|
||||
FogAuthenticator() = default;
|
||||
~FogAuthenticator() = default;
|
||||
|
||||
virtual void init();
|
||||
|
||||
static void preload();
|
||||
|
||||
Maybe<void> authenticateAgent() override;
|
||||
void setAddressExtenesion(const std::string &extension) override;
|
||||
|
||||
protected:
|
||||
class UserCredentials
|
||||
{
|
||||
public:
|
||||
UserCredentials() = default;
|
||||
UserCredentials(const std::string &client_id, const std::string &shared_secret);
|
||||
|
||||
std::string getClientId() const { return client_id; }
|
||||
std::string getSharedSecret() const { return shared_secret; }
|
||||
|
||||
void serialize(cereal::JSONOutputArchive &out_ar) const;
|
||||
void serialize(cereal::JSONInputArchive &in_ar);
|
||||
|
||||
private:
|
||||
std::string client_id;
|
||||
std::string shared_secret;
|
||||
};
|
||||
|
||||
void loadRequiredSecurityApps();
|
||||
Maybe<AccessToken> getAccessToken(const UserCredentials &credentials) const;
|
||||
Maybe<UserCredentials>
|
||||
registerAgent(
|
||||
const RegistrationData ®_data,
|
||||
const std::string &name,
|
||||
const std::string &type,
|
||||
const std::string &platform,
|
||||
const std::string &architecture
|
||||
) const;
|
||||
|
||||
void initRestAPI();
|
||||
Maybe<UserCredentials> getCredentials();
|
||||
|
||||
bool saveCredentialsToFile(const UserCredentials &credentials) const;
|
||||
Maybe<UserCredentials> getCredentialsFromFile() const;
|
||||
Maybe<RegistrationData> getRegistrationData();
|
||||
|
||||
std::string base64Encode(const std::string &in) const;
|
||||
std::string buildBasicAuthHeader(const std::string &username, const std::string &pass) const;
|
||||
std::string buildOAuth2Header(const std::string &token) const;
|
||||
|
||||
// This apps which the orchestrations requires them from Fog.
|
||||
std::vector<std::string> required_security_apps;
|
||||
std::string fog_address_ex = "";
|
||||
std::string filesystem_prefix = "";
|
||||
std::string otp = "";
|
||||
Maybe<UserCredentials> credentials = genError("User credentials are empty");
|
||||
Maybe<AccessToken> access_token = genError("Access token was not received yet");
|
||||
Maybe<RegistrationData> reg_data = genError("Registration data is empty");
|
||||
I_MainLoop::RoutineID routine = 0;
|
||||
};
|
||||
|
||||
class AdditionalMetaData
|
||||
{
|
||||
public:
|
||||
AdditionalMetaData() = default;
|
||||
AdditionalMetaData(const AdditionalMetaData &) = default;
|
||||
|
||||
AdditionalMetaData &
|
||||
operator<<(const std::pair<std::string, std::string> &data)
|
||||
{
|
||||
additional_data.insert(data);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void
|
||||
serialize(cereal::JSONOutputArchive &out_ar) const
|
||||
{
|
||||
for (auto &data : additional_data) {
|
||||
out_ar(cereal::make_nvp(data.first, data.second));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::map<std::string, std::string> additional_data;
|
||||
};
|
||||
|
||||
class RegistrationRequest : public ClientRest
|
||||
{
|
||||
private:
|
||||
class MetaData
|
||||
{
|
||||
public:
|
||||
MetaData() = default;
|
||||
MetaData(
|
||||
const std::string &_name,
|
||||
const std::string &_type,
|
||||
const std::string &_platform,
|
||||
const std::string &_architecture,
|
||||
const std::string &_agent_version)
|
||||
:
|
||||
name(_name),
|
||||
type(_type),
|
||||
platform(_platform),
|
||||
architecture(_architecture),
|
||||
agent_version(_agent_version)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
serialize(cereal::JSONOutputArchive &out_ar) const
|
||||
{
|
||||
out_ar(
|
||||
cereal::make_nvp("agentName", name),
|
||||
cereal::make_nvp("agentType", type),
|
||||
cereal::make_nvp("platform", platform),
|
||||
cereal::make_nvp("architecture", architecture),
|
||||
cereal::make_nvp("agentVersion", agent_version),
|
||||
cereal::make_nvp("additionalMetaData", additional_metadata)
|
||||
);
|
||||
}
|
||||
|
||||
AdditionalMetaData &
|
||||
operator<<(const std::pair<std::string, std::string> &data)
|
||||
{
|
||||
return additional_metadata << data;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string name;
|
||||
std::string type;
|
||||
std::string platform;
|
||||
std::string architecture;
|
||||
std::string agent_version;
|
||||
AdditionalMetaData additional_metadata;
|
||||
};
|
||||
|
||||
public:
|
||||
RegistrationRequest(
|
||||
const FogAuthenticator::RegistrationData ®_data,
|
||||
const std::string &name,
|
||||
const std::string &type,
|
||||
const std::string &platform,
|
||||
const std::string &architecture,
|
||||
const std::string &agent_version)
|
||||
:
|
||||
authenticationData({ reg_data }),
|
||||
metaData(MetaData(name, type, platform, architecture, agent_version))
|
||||
{
|
||||
}
|
||||
|
||||
AdditionalMetaData &
|
||||
operator<<(const std::pair<std::string, std::string> &data)
|
||||
{
|
||||
return metaData.get() << data;
|
||||
}
|
||||
|
||||
std::string getClientId() const { return client_id; }
|
||||
std::string getSharedSecret() const { return shared_secret; }
|
||||
std::string getAgentId() const { return agentId; }
|
||||
std::string getProfileId() const { return profileId; }
|
||||
std::string getTenantId() const { return tenantId; }
|
||||
|
||||
private:
|
||||
C2S_PARAM(std::vector<FogAuthenticator::RegistrationData>, authenticationData);
|
||||
C2S_PARAM(MetaData, metaData);
|
||||
|
||||
S2C_PARAM(std::string, client_id);
|
||||
S2C_PARAM(std::string, shared_secret);
|
||||
S2C_PARAM(std::string, tenantId);
|
||||
S2C_PARAM(std::string, profileId);
|
||||
S2C_PARAM(std::string, agentId);
|
||||
};
|
||||
|
||||
class PolicyVersionPatchRequest : public ClientRest
|
||||
{
|
||||
public:
|
||||
PolicyVersionPatchRequest(const std::string &_policy_version)
|
||||
:
|
||||
policy_version(_policy_version)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
C2S_LABEL_PARAM(std::string, policy_version, "policyVersion");
|
||||
};
|
||||
|
||||
class TokenRequest : public ClientRest
|
||||
{
|
||||
public:
|
||||
std::string getAccessToken() const { return access_token; }
|
||||
std::string getTokenType() const { return token_type; }
|
||||
std::string getUserId() const { return user_id; }
|
||||
std::string getScope() const { return scope; }
|
||||
std::string getJTI() const { return jti; }
|
||||
int getExpirationTime() const { return expires_in; }
|
||||
|
||||
private:
|
||||
S2C_PARAM(int, expires_in);
|
||||
S2C_PARAM(std::string, jti);
|
||||
S2C_PARAM(std::string, scope);
|
||||
S2C_PARAM(std::string, token_type);
|
||||
S2C_PARAM(std::string, access_token);
|
||||
S2C_LABEL_PARAM(std::string, user_id, "uuid");
|
||||
};
|
||||
|
||||
#endif // __FOG_AUTHENTICATOR_H__
|
||||
45
components/security_apps/orchestration/include/fog_communication.h
Executable file
45
components/security_apps/orchestration/include/fog_communication.h
Executable 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 __FOG_COMMUNICATION_H__
|
||||
#define __FOG_COMMUNICATION_H__
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
#include "cereal/archives/json.hpp"
|
||||
|
||||
#include "i_update_communication.h"
|
||||
#include "fog_authenticator.h"
|
||||
#include "i_orchestration_tools.h"
|
||||
#include "i_agent_details.h"
|
||||
#include "i_orchestration_status.h"
|
||||
#include "i_messaging.h"
|
||||
#include "i_mainloop.h"
|
||||
#include "i_encryptor.h"
|
||||
#include "i_details_resolver.h"
|
||||
#include "i_rest_api.h"
|
||||
#include "i_time_get.h"
|
||||
#include "i_encryptor.h"
|
||||
#include "maybe_res.h"
|
||||
|
||||
class FogCommunication : public FogAuthenticator
|
||||
{
|
||||
public:
|
||||
Maybe<void> getUpdate(CheckUpdateRequest &request) override;
|
||||
Maybe<std::string> downloadAttributeFile(const GetResourceFile &resourse_file) override;
|
||||
Maybe<void> sendPolicyVersion(const std::string &policy_version) const override;
|
||||
};
|
||||
|
||||
#endif // __FOG_COMMUNICATION_H__
|
||||
91
components/security_apps/orchestration/include/get_status_rest.h
Executable file
91
components/security_apps/orchestration/include/get_status_rest.h
Executable file
@@ -0,0 +1,91 @@
|
||||
// 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 __GET_STATUS_RES_H__
|
||||
#define __GET_STATUS_RES_H__
|
||||
|
||||
#include "i_messaging_downloader.h"
|
||||
#include "i_messaging.h"
|
||||
#include "i_mainloop.h"
|
||||
#include "i_shell_cmd.h"
|
||||
#include "i_encryptor.h"
|
||||
#include "i_orchestration_status.h"
|
||||
#include "i_rest_api.h"
|
||||
#include "i_orchestration_tools.h"
|
||||
#include "i_downloader.h"
|
||||
#include "i_service_controller.h"
|
||||
#include "i_manifest_controller.h"
|
||||
#include "i_update_communication.h"
|
||||
#include "i_details_resolver.h"
|
||||
#include "i_shell_cmd.h"
|
||||
#include "i_agent_details.h"
|
||||
#include "i_environment.h"
|
||||
#include "i_tenant_manager.h"
|
||||
#include "i_package_handler.h"
|
||||
#include "component.h"
|
||||
|
||||
class getStatusRest : public ServerRest
|
||||
{
|
||||
public:
|
||||
void
|
||||
doCall() override
|
||||
{
|
||||
auto i_orch_status = Singleton::Consume<I_OrchestrationStatus>::by<OrchestrationComp>();
|
||||
|
||||
policies = "";
|
||||
settings = "";
|
||||
for (auto &policy: i_orch_status->getServicePolicies()) {
|
||||
policies = policies.get() + "\n " + policy.first + ": " + policy.second;
|
||||
}
|
||||
for (auto &setting: i_orch_status->getServiceSettings()) {
|
||||
settings = settings.get() + "\n " + setting.first + ": " + setting.second;
|
||||
}
|
||||
|
||||
last_update_attempt = i_orch_status->getLastUpdateAttempt();
|
||||
last_update = i_orch_status->getUpdateTime();
|
||||
last_update_status = i_orch_status->getUpdateStatus();
|
||||
policy_version = i_orch_status->getPolicyVersion();
|
||||
last_policy_update = i_orch_status->getLastPolicyUpdate();
|
||||
last_manifest_update = i_orch_status->getLastManifestUpdate();
|
||||
last_settings_update = i_orch_status->getLastSettingsUpdate();
|
||||
registration_status = i_orch_status->getRegistrationStatus();
|
||||
manifest_status = i_orch_status->getManifestStatus();
|
||||
upgrade_mode = i_orch_status->getUpgradeMode();
|
||||
fog_address = i_orch_status->getFogAddress();
|
||||
agent_id = i_orch_status->getAgentId();
|
||||
profile_id = i_orch_status->getProfileId();
|
||||
tenant_id = i_orch_status->getTenantId();
|
||||
registration_details = i_orch_status->getRegistrationDetails();
|
||||
}
|
||||
|
||||
private:
|
||||
S2C_LABEL_PARAM(std::string, last_update_attempt, "Last update attempt");
|
||||
S2C_LABEL_PARAM(std::string, last_update, "Last update");
|
||||
S2C_LABEL_PARAM(std::string, last_update_status, "Last update status");
|
||||
S2C_LABEL_PARAM(std::string, policy_version, "Policy version");
|
||||
S2C_LABEL_PARAM(std::string, last_policy_update, "Last policy update");
|
||||
S2C_LABEL_PARAM(std::string, last_manifest_update, "Last manifest update");
|
||||
S2C_LABEL_PARAM(std::string, last_settings_update, "Last settings update");
|
||||
S2C_LABEL_PARAM(std::string, registration_status, "Registration status");
|
||||
S2C_LABEL_PARAM(std::string, manifest_status, "Manifest status");
|
||||
S2C_LABEL_PARAM(std::string, upgrade_mode, "Upgrade mode");
|
||||
S2C_LABEL_PARAM(std::string, fog_address, "Fog address");
|
||||
S2C_LABEL_PARAM(std::string, agent_id, "Agent ID");
|
||||
S2C_LABEL_PARAM(std::string, profile_id, "Profile ID");
|
||||
S2C_LABEL_PARAM(std::string, tenant_id, "Tenant ID");
|
||||
S2C_LABEL_PARAM(std::string, registration_details, "Registration details");
|
||||
S2C_LABEL_PARAM(std::string, policies, "Service policy");
|
||||
S2C_LABEL_PARAM(std::string, settings, "Service settings");
|
||||
};
|
||||
|
||||
#endif // __GET_STATUS_RES_H__
|
||||
58
components/security_apps/orchestration/include/hybrid_communication.h
Executable file
58
components/security_apps/orchestration/include/hybrid_communication.h
Executable file
@@ -0,0 +1,58 @@
|
||||
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef __HYBRID_COMMUNICATION_H__
|
||||
#define __HYBRID_COMMUNICATION_H__
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
#include "cereal/archives/json.hpp"
|
||||
|
||||
#include "singleton.h"
|
||||
#include "i_update_communication.h"
|
||||
#include "fog_authenticator.h"
|
||||
#include "i_k8s_policy_gen.h"
|
||||
#include "i_orchestration_tools.h"
|
||||
#include "i_agent_details.h"
|
||||
#include "i_orchestration_status.h"
|
||||
#include "i_messaging.h"
|
||||
#include "i_mainloop.h"
|
||||
#include "i_encryptor.h"
|
||||
#include "i_details_resolver.h"
|
||||
#include "i_rest_api.h"
|
||||
#include "i_time_get.h"
|
||||
#include "i_encryptor.h"
|
||||
#include "maybe_res.h"
|
||||
|
||||
class HybridCommunication
|
||||
:
|
||||
public FogAuthenticator,
|
||||
Singleton::Consume<I_K8S_Policy_Gen>
|
||||
{
|
||||
public:
|
||||
virtual void init() override;
|
||||
Maybe<void> getUpdate(CheckUpdateRequest &request) override;
|
||||
Maybe<std::string> downloadAttributeFile(const GetResourceFile &resourse_file) override;
|
||||
Maybe<void> sendPolicyVersion(const std::string &policy_version) const override;
|
||||
std::string getChecksum(const std::string &policy_version);
|
||||
|
||||
private:
|
||||
Maybe<std::string> getNewVersion();
|
||||
|
||||
std::string curr_version;
|
||||
std::string curr_policy;
|
||||
};
|
||||
|
||||
#endif // __HYBRID_COMMUNICATION_H__
|
||||
43
components/security_apps/orchestration/include/local_communication.h
Executable file
43
components/security_apps/orchestration/include/local_communication.h
Executable file
@@ -0,0 +1,43 @@
|
||||
// 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 __LOCAL_COMMUNICATION_H__
|
||||
#define __LOCAL_COMMUNICATION_H__
|
||||
|
||||
#include "i_update_communication.h"
|
||||
#include "i_orchestration_tools.h"
|
||||
#include "maybe_res.h"
|
||||
|
||||
class LocalCommunication
|
||||
:
|
||||
public I_UpdateCommunication,
|
||||
Singleton::Consume<I_OrchestrationTools>
|
||||
{
|
||||
public:
|
||||
static void preload();
|
||||
|
||||
void init();
|
||||
|
||||
Maybe<void> authenticateAgent() override;
|
||||
Maybe<void> getUpdate(CheckUpdateRequest &request) override;
|
||||
|
||||
Maybe<std::string> downloadAttributeFile(const GetResourceFile &resourse_file) override;
|
||||
void setAddressExtenesion(const std::string &extension) override;
|
||||
Maybe<void> sendPolicyVersion(const std::string &policy_version) const override;
|
||||
|
||||
private:
|
||||
std::string getChecksum(const std::string &file_path);
|
||||
std::string filesystem_prefix = "";
|
||||
};
|
||||
|
||||
#endif // __LOCAL_COMMUNICATION_H__
|
||||
@@ -0,0 +1,46 @@
|
||||
// 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 __MOCK_DETAILS_RESOLVER_H__
|
||||
#define __MOCK_DETAILS_RESOLVER_H__
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "i_details_resolver.h"
|
||||
#include "cptest.h"
|
||||
#include "maybe_res.h"
|
||||
|
||||
std::ostream &
|
||||
operator<<(std::ostream &os, const Maybe<std::tuple<std::string, std::string, std::string>> &)
|
||||
{
|
||||
return os;
|
||||
}
|
||||
|
||||
class MockDetailsResolver
|
||||
:
|
||||
public Singleton::Provide<I_DetailsResolver>::From<MockProvider<I_DetailsResolver>>
|
||||
{
|
||||
public:
|
||||
MOCK_METHOD0(getHostname, Maybe<std::string>());
|
||||
MOCK_METHOD0(getPlatform, Maybe<std::string>());
|
||||
MOCK_METHOD0(getArch, Maybe<std::string>());
|
||||
MOCK_METHOD0(getAgentVersion, std::string());
|
||||
MOCK_METHOD0(isReverseProxy, bool());
|
||||
MOCK_METHOD0(isKernelVersion3OrHigher, bool());
|
||||
MOCK_METHOD0(isGwNotVsx, bool());
|
||||
MOCK_METHOD0(getResolvedDetails, std::map<std::string, std::string>());
|
||||
MOCK_METHOD0(isVersionEqualOrAboveR8110, bool());
|
||||
MOCK_METHOD0(parseNginxMetadata, Maybe<std::tuple<std::string, std::string, std::string>>());
|
||||
};
|
||||
|
||||
#endif // __MOCK_DETAILS_RESOLVER_H__
|
||||
42
components/security_apps/orchestration/include/mock/mock_downloader.h
Executable file
42
components/security_apps/orchestration/include/mock/mock_downloader.h
Executable file
@@ -0,0 +1,42 @@
|
||||
// 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 __MOCK_DOWNLOADER_H__
|
||||
#define __MOCK_DOWNLOADER_H__
|
||||
|
||||
#include "cptest.h"
|
||||
#include "i_downloader.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
class MockDownloader :
|
||||
public Singleton::Provide<I_Downloader>::From<MockProvider<I_Downloader>>
|
||||
{
|
||||
public:
|
||||
MOCK_CONST_METHOD3(
|
||||
downloadFileFromFog,
|
||||
Maybe<std::string>(const std::string &, Package::ChecksumTypes, const GetResourceFile &)
|
||||
);
|
||||
|
||||
MOCK_CONST_METHOD2(
|
||||
downloadVirtualFileFromFog,
|
||||
Maybe<std::map<std::string, std::string>>(const GetResourceFile &, Package::ChecksumTypes)
|
||||
);
|
||||
|
||||
MOCK_CONST_METHOD4(
|
||||
downloadFileFromURL,
|
||||
Maybe<std::string>(const std::string &, const std::string &, Package::ChecksumTypes, const std::string &)
|
||||
);
|
||||
};
|
||||
|
||||
#endif // __MOCK_DOWNLOADER_H__
|
||||
@@ -0,0 +1,28 @@
|
||||
// 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 __MOCK_MANIFEST_CONTROLLER_H__
|
||||
#define __MOCK_MANIFEST_CONTROLLER_H__
|
||||
|
||||
#include "i_manifest_controller.h"
|
||||
#include "cptest.h"
|
||||
|
||||
class MockManifestController :
|
||||
public Singleton::Provide<I_ManifestController>::From<MockProvider<I_ManifestController>>
|
||||
{
|
||||
public:
|
||||
MOCK_METHOD1(updateManifest, bool(const std::string &));
|
||||
MOCK_METHOD0(loadAfterSelfUpdate, bool());
|
||||
};
|
||||
|
||||
#endif // __MOCK_MANIFEST_CONTROLLER_H__
|
||||
@@ -0,0 +1,39 @@
|
||||
// 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 __MOCK_MESSAGING_DOWNLOADER_H__
|
||||
#define __MOCK_MESSAGING_DOWNLOADER_H__
|
||||
|
||||
#include "cptest.h"
|
||||
#include <string>
|
||||
|
||||
#include "i_messaging_downloader.h"
|
||||
|
||||
class MockMessagingDownloader
|
||||
:
|
||||
public Singleton::Provide<I_MessagingDownloader>::From<MockProvider<I_MessagingDownloader>>
|
||||
{
|
||||
public:
|
||||
MOCK_METHOD4(
|
||||
downloadFile,
|
||||
bool(
|
||||
const std::string &,
|
||||
const std::string &,
|
||||
OnCompleteCB,
|
||||
const unsigned int
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
#endif // __MOCK_MESSAGING_DOWNLOADER_H__
|
||||
@@ -0,0 +1,63 @@
|
||||
// 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 __MOCK_ORCHESTRATION_STATUS_H__
|
||||
#define __MOCK_ORCHESTRATION_STATUS_H__
|
||||
|
||||
#include "i_orchestration_status.h"
|
||||
#include "cptest.h"
|
||||
|
||||
class MockOrchestrationStatus
|
||||
:
|
||||
public Singleton::Provide<I_OrchestrationStatus>::From<MockProvider<I_OrchestrationStatus>>
|
||||
{
|
||||
public:
|
||||
MOCK_METHOD0(writeStatusToFile, void());
|
||||
MOCK_METHOD0(recoverFields, void());
|
||||
MOCK_METHOD1(setUpgradeMode, void(const std::string &));
|
||||
MOCK_METHOD1(setAgentType, void(const std::string &));
|
||||
MOCK_METHOD1(setRegistrationStatus, void(const std::string &));
|
||||
MOCK_METHOD1(setFogAddress, void(const std::string &));
|
||||
MOCK_METHOD1(setPolicyVersion, void(const std::string &));
|
||||
MOCK_METHOD1(setIsConfigurationUpdated, void(EnumArray<OrchestrationStatusConfigType, bool> config_types));
|
||||
MOCK_METHOD0(setLastUpdateAttempt, void());
|
||||
MOCK_METHOD3(setAgentDetails, void(const std::string &, const std::string &, const std::string &));
|
||||
MOCK_METHOD3(setFieldStatus,
|
||||
void(const OrchestrationStatusFieldType &, const OrchestrationStatusResult &, const std::string &));
|
||||
MOCK_METHOD4(setRegistrationDetails,
|
||||
void(const std::string &, const std::string &, const std::string &, const std::string &)
|
||||
);
|
||||
MOCK_METHOD3(setServiceConfiguration,
|
||||
void(const std::string &, const std::string &, const OrchestrationStatusConfigType &)
|
||||
);
|
||||
MOCK_CONST_METHOD0(getLastUpdateAttempt, const std::string&());
|
||||
MOCK_CONST_METHOD0(getUpdateStatus, const std::string&());
|
||||
MOCK_CONST_METHOD0(getUpdateTime, const std::string&());
|
||||
MOCK_CONST_METHOD0(getLastManifestUpdate, const std::string&());
|
||||
MOCK_CONST_METHOD0(getPolicyVersion, const std::string&());
|
||||
MOCK_CONST_METHOD0(getLastPolicyUpdate, const std::string&());
|
||||
MOCK_CONST_METHOD0(getLastSettingsUpdate, const std::string&());
|
||||
MOCK_CONST_METHOD0(getUpgradeMode, const std::string&());
|
||||
MOCK_CONST_METHOD0(getFogAddress, const std::string&());
|
||||
MOCK_CONST_METHOD0(getRegistrationStatus, const std::string&());
|
||||
MOCK_CONST_METHOD0(getAgentId, const std::string&());
|
||||
MOCK_CONST_METHOD0(getProfileId, const std::string&());
|
||||
MOCK_CONST_METHOD0(getTenantId, const std::string&());
|
||||
MOCK_CONST_METHOD0(getManifestStatus, const std::string&());
|
||||
MOCK_CONST_METHOD0(getManifestError, const std::string&());
|
||||
MOCK_CONST_METHOD0(getServicePolicies, const std::map<std::string, std::string>&());
|
||||
MOCK_CONST_METHOD0(getServiceSettings, const std::map<std::string, std::string>&());
|
||||
MOCK_CONST_METHOD0(getRegistrationDetails, const std::string());
|
||||
};
|
||||
|
||||
#endif // __MOCK_ORCHESTRATION_STATUS_H__
|
||||
@@ -0,0 +1,58 @@
|
||||
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef __MOCK_ORCHESTRATION_TOOLS_H__
|
||||
#define __MOCK_ORCHESTRATION_TOOLS_H__
|
||||
|
||||
#include "cptest.h"
|
||||
#include "i_orchestration_tools.h"
|
||||
|
||||
template <typename T>
|
||||
std::ostream &
|
||||
operator<<(std::ostream &os, const std::vector<T> &)
|
||||
{
|
||||
return os;
|
||||
}
|
||||
|
||||
template <typename T, typename S>
|
||||
std::ostream &
|
||||
operator<<(std::ostream &os, const std::map<T, S> &)
|
||||
{
|
||||
return os;
|
||||
}
|
||||
|
||||
class MockOrchestrationTools
|
||||
:
|
||||
public Singleton::Provide<I_OrchestrationTools>::From<MockProvider<I_OrchestrationTools>>
|
||||
{
|
||||
public:
|
||||
MOCK_CONST_METHOD1(loadPackagesFromJson, Maybe<std::map<std::string, Package>>(const std::string &));
|
||||
MOCK_CONST_METHOD2(packagesToJsonFile, bool(const std::map<std::string, Package> &, const std::string &));
|
||||
MOCK_CONST_METHOD1(isNonEmptyFile, bool(const std::string &));
|
||||
MOCK_CONST_METHOD1(readFile, Maybe<std::string>(const std::string &));
|
||||
MOCK_CONST_METHOD2(writeFile, bool(const std::string &, const std::string &));
|
||||
MOCK_CONST_METHOD1(removeFile, bool(const std::string &));
|
||||
MOCK_CONST_METHOD2(copyFile, bool(const std::string &, const std::string &));
|
||||
MOCK_CONST_METHOD2(calculateChecksum, Maybe<std::string>(Package::ChecksumTypes, const std::string &));
|
||||
MOCK_CONST_METHOD2(
|
||||
jsonObjectSplitter,
|
||||
Maybe<std::map<std::string, std::string>>(const std::string &, const std::string &)
|
||||
);
|
||||
MOCK_CONST_METHOD1(doesFileExist, bool(const std::string &));
|
||||
MOCK_CONST_METHOD1(createDirectory, bool(const std::string &));
|
||||
MOCK_CONST_METHOD1(doesDirectoryExist, bool(const std::string &));
|
||||
MOCK_CONST_METHOD1(executeCmd, bool(const std::string &));
|
||||
MOCK_CONST_METHOD1(base64Encode, std::string(const std::string &));
|
||||
MOCK_CONST_METHOD1(base64Decode, std::string(const std::string &));
|
||||
};
|
||||
#endif // __MOCK_ORCHESTRATION_TOOLS_H__
|
||||
31
components/security_apps/orchestration/include/mock/mock_package_handler.h
Executable file
31
components/security_apps/orchestration/include/mock/mock_package_handler.h
Executable file
@@ -0,0 +1,31 @@
|
||||
// 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 __MOCK_PACKAGE_HANDLER_H__
|
||||
#define __MOCK_PACKAGE_HANDLER_H__
|
||||
|
||||
#include "i_package_handler.h"
|
||||
#include "cptest.h"
|
||||
|
||||
class MockPackageHandler :
|
||||
public Singleton::Provide<I_PackageHandler>::From<MockProvider<I_PackageHandler>>
|
||||
{
|
||||
public:
|
||||
MOCK_CONST_METHOD3(installPackage, bool(const std::string &, const std::string &, bool));
|
||||
MOCK_CONST_METHOD3(uninstallPackage, bool(const std::string &, const std::string &, const std::string &));
|
||||
MOCK_CONST_METHOD2(preInstallPackage, bool(const std::string &, const std::string &));
|
||||
MOCK_CONST_METHOD2(postInstallPackage, bool(const std::string &, const std::string &));
|
||||
MOCK_CONST_METHOD2(updateSavedPackage, bool(const std::string &, const std::string &));
|
||||
MOCK_CONST_METHOD2(shouldInstallPackage, bool(const std::string &, const std::string &));
|
||||
};
|
||||
#endif // __MOCK_PACKAGE_HANDLER_H__
|
||||
@@ -0,0 +1,62 @@
|
||||
// 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 __MOCK_SERVICE_CONTROLLER_H__
|
||||
#define __MOCK_SERVICE_CONTROLLER_H__
|
||||
|
||||
#include "i_service_controller.h"
|
||||
|
||||
#include "cptest.h"
|
||||
#include <string>
|
||||
|
||||
class MockServiceController :
|
||||
public Singleton::Provide<I_ServiceController>::From<MockProvider<I_ServiceController>>
|
||||
|
||||
{
|
||||
public:
|
||||
MOCK_CONST_METHOD0(getPolicyVersion, const std::string &());
|
||||
|
||||
MOCK_CONST_METHOD0(getUpdatePolicyVersion, const std::string &());
|
||||
|
||||
MOCK_METHOD4(
|
||||
updateServiceConfiguration,
|
||||
bool(
|
||||
const std::string &new_policy_path,
|
||||
const std::string &new_settings_path,
|
||||
const std::vector<std::string> &new_data_files,
|
||||
const std::string &tenant_id
|
||||
)
|
||||
);
|
||||
|
||||
MOCK_METHOD1(isServiceInstalled, bool(const std::string &service_name));
|
||||
|
||||
MOCK_METHOD4(
|
||||
registerServiceConfig,
|
||||
void(
|
||||
const std::string &service_name,
|
||||
PortNumber listening_port,
|
||||
const std::vector<std::string> &expected_configurations,
|
||||
const std::string &id
|
||||
)
|
||||
);
|
||||
|
||||
typedef std::map<std::string, PortNumber> ServicePortMap;
|
||||
MOCK_METHOD0(getServiceToPortMap, ServicePortMap());
|
||||
MOCK_METHOD2(updateReconfStatus, void(int id, ReconfStatus status));
|
||||
MOCK_METHOD4(
|
||||
startReconfStatus,
|
||||
void(int id, ReconfStatus status, const std::string &serivce_name, const std::string &service_id)
|
||||
);
|
||||
};
|
||||
|
||||
#endif // __MOCK_SERVICE_CONTROLLER_H__
|
||||
@@ -0,0 +1,37 @@
|
||||
// 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 __MOCK_UPDATE_COMMUNICATION_H__
|
||||
#define __MOCK_UPDATE_COMMUNICATION_H__
|
||||
|
||||
#include "i_update_communication.h"
|
||||
#include "cptest.h"
|
||||
|
||||
std::ostream &
|
||||
operator<<(std::ostream &os, const CheckUpdateRequest &)
|
||||
{
|
||||
return os;
|
||||
}
|
||||
|
||||
class MockUpdateCommunication :
|
||||
public Singleton::Provide<I_UpdateCommunication>::From<MockProvider<I_UpdateCommunication>>
|
||||
{
|
||||
public:
|
||||
MOCK_METHOD0(authenticateAgent, Maybe<void>());
|
||||
MOCK_METHOD1(getUpdate, Maybe<void>(CheckUpdateRequest &));
|
||||
MOCK_METHOD1(downloadAttributeFile, Maybe<std::string>(const GetResourceFile &));
|
||||
MOCK_METHOD1(setAddressExtenesion, void(const std::string &));
|
||||
MOCK_CONST_METHOD1(sendPolicyVersion, Maybe<void>(const std::string &));
|
||||
};
|
||||
|
||||
#endif // __MOCK_UPDATE_COMMUNICATION_H__
|
||||
38
components/security_apps/orchestration/include/orchestration_policy.h
Executable file
38
components/security_apps/orchestration/include/orchestration_policy.h
Executable file
@@ -0,0 +1,38 @@
|
||||
// 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 __ORCHESTRATION_POLICY_H__
|
||||
#define __ORCHESTRATION_POLICY_H__
|
||||
|
||||
#include <string>
|
||||
#include "cereal/archives/json.hpp"
|
||||
|
||||
class OrchestrationPolicy
|
||||
{
|
||||
public:
|
||||
const std::string & getFogAddress() const;
|
||||
const unsigned long & getSleepInterval() const;
|
||||
const unsigned long & getErrorSleepInterval() const;
|
||||
|
||||
void serialize(cereal::JSONInputArchive & archive);
|
||||
|
||||
bool operator==(const OrchestrationPolicy &other) const;
|
||||
bool operator!=(const OrchestrationPolicy &other) const;
|
||||
|
||||
private:
|
||||
std::string fog_address;
|
||||
unsigned long sleep_interval;
|
||||
unsigned long error_sleep_interval;
|
||||
};
|
||||
|
||||
#endif // __ORCHESTRATION_POLICY_H__
|
||||
@@ -0,0 +1 @@
|
||||
add_library(k8s_policy_gen k8s_policy_gen.cc)
|
||||
@@ -0,0 +1,768 @@
|
||||
// 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 __APPSEC_PRACTICE_SECTION_H__
|
||||
#define __APPSEC_PRACTICE_SECTION_H__
|
||||
|
||||
#include <cereal/archives/json.hpp>
|
||||
#include <boost/uuid/uuid.hpp>
|
||||
#include <boost/uuid/uuid_generators.hpp>
|
||||
#include <boost/uuid/uuid_io.hpp>
|
||||
|
||||
#include "config.h"
|
||||
#include "debug.h"
|
||||
#include "customized_cereal_map.h"
|
||||
#include "k8s_policy_common.h"
|
||||
#include "triggers_section.h"
|
||||
#include "trusted_sources_section.h"
|
||||
|
||||
USE_DEBUG_FLAG(D_K8S_POLICY);
|
||||
|
||||
class AppSecWebBotsURI
|
||||
{
|
||||
public:
|
||||
void
|
||||
load(cereal::JSONInputArchive &archive_in)
|
||||
{
|
||||
dbgTrace(D_K8S_POLICY) << "Loading AppSec Web Bots URI";
|
||||
parseAppsecJSONKey<std::string>("uri", uri, archive_in);
|
||||
}
|
||||
|
||||
const std::string & getURI() const { return uri; }
|
||||
|
||||
private:
|
||||
std::string uri;
|
||||
};
|
||||
|
||||
std::ostream &
|
||||
operator<<(std::ostream &os, const AppSecWebBotsURI &obj)
|
||||
{
|
||||
os << obj.getURI();
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream &
|
||||
operator<<(std::ostream &os, const std::vector<AppSecWebBotsURI> &obj)
|
||||
{
|
||||
os << "[" << std::endl;
|
||||
makeSeparatedStr(obj, ",");
|
||||
os << std::endl << "]";
|
||||
return os;
|
||||
}
|
||||
|
||||
class AppSecPracticeAntiBot
|
||||
{
|
||||
public:
|
||||
void
|
||||
load(cereal::JSONInputArchive &archive_in)
|
||||
{
|
||||
dbgTrace(D_K8S_POLICY) << "Loading AppSec Web Bots";
|
||||
parseAppsecJSONKey<std::vector<AppSecWebBotsURI>>("injected-URIs", injected_uris, archive_in);
|
||||
parseAppsecJSONKey<std::vector<AppSecWebBotsURI>>("validated-URIs", validated_uris, archive_in);
|
||||
parseAppsecJSONKey<std::string>("override-mode", override_mode, archive_in, "Inactive");
|
||||
}
|
||||
|
||||
void
|
||||
save(cereal::JSONOutputArchive &out_ar) const
|
||||
{
|
||||
std::vector<std::string> injected;
|
||||
std::vector<std::string> validated;
|
||||
for (const AppSecWebBotsURI &uri : getInjectedURIs()) injected.push_back(uri.getURI());
|
||||
for (const AppSecWebBotsURI &uri : getValidatedURIs()) injected.push_back(uri.getURI());
|
||||
out_ar(
|
||||
cereal::make_nvp("injected", injected),
|
||||
cereal::make_nvp("validated", validated)
|
||||
);
|
||||
}
|
||||
|
||||
const std::vector<AppSecWebBotsURI> & getInjectedURIs() const { return injected_uris; }
|
||||
const std::vector<AppSecWebBotsURI> & getValidatedURIs() const { return validated_uris; }
|
||||
const std::string & getOverrideMode() const { return override_mode; }
|
||||
|
||||
private:
|
||||
std::string override_mode;
|
||||
std::vector<AppSecWebBotsURI> injected_uris;
|
||||
std::vector<AppSecWebBotsURI> validated_uris;
|
||||
};
|
||||
|
||||
std::ostream &
|
||||
operator<<(std::ostream &os, const AppSecPracticeAntiBot &obj)
|
||||
{
|
||||
os
|
||||
<< "injected-URIs: "
|
||||
<< obj.getInjectedURIs()
|
||||
<< " validated-URIs: "
|
||||
<< obj.getValidatedURIs()
|
||||
<< ", override_mode: "
|
||||
<< obj.getOverrideMode();
|
||||
return os;
|
||||
}
|
||||
|
||||
class AppSecWebAttackProtections
|
||||
{
|
||||
public:
|
||||
void
|
||||
load(cereal::JSONInputArchive &archive_in)
|
||||
{
|
||||
dbgTrace(D_K8S_POLICY) << "Loading AppSec Web Attack Protections";
|
||||
parseAppsecJSONKey<std::string>("csrf-protection", csrf_protection, archive_in, "Inactive");
|
||||
parseAppsecJSONKey<std::string>("error-disclosure", error_disclosure, archive_in, "Inactive");
|
||||
parseAppsecJSONKey<std::string>("open-redirect", open_redirect, archive_in, "Inactive");
|
||||
parseAppsecJSONKey<bool>("non-valid-http-methods", non_valid_http_methods, archive_in, false);
|
||||
}
|
||||
|
||||
const std::string
|
||||
getCsrfProtectionMode() const
|
||||
{
|
||||
if (key_to_practices_val.find(csrf_protection) == key_to_practices_val.end()) {
|
||||
dbgError(D_K8S_POLICY)
|
||||
<< "Failed to find a value for "
|
||||
<< csrf_protection
|
||||
<< ". Setting CSRF protection to Inactive";
|
||||
return "Inactive";
|
||||
}
|
||||
return key_to_practices_val.at(csrf_protection);
|
||||
}
|
||||
|
||||
const std::string & getErrorDisclosureMode() const { return error_disclosure; }
|
||||
|
||||
bool getNonValidHttpMethods() const { return non_valid_http_methods; }
|
||||
|
||||
const std::string
|
||||
getOpenRedirectMode() const
|
||||
{
|
||||
if (key_to_practices_val.find(open_redirect) == key_to_practices_val.end()) {
|
||||
dbgError(D_K8S_POLICY)
|
||||
<< "Failed to find a value for "
|
||||
<< open_redirect
|
||||
<< ". Setting Open Redirect mode to Inactive";
|
||||
return "Inactive";
|
||||
}
|
||||
return key_to_practices_val.at(open_redirect);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string csrf_protection;
|
||||
std::string open_redirect;
|
||||
std::string error_disclosure;
|
||||
bool non_valid_http_methods;
|
||||
};
|
||||
|
||||
std::ostream &
|
||||
operator<<(std::ostream &os, const AppSecWebAttackProtections &obj)
|
||||
{
|
||||
os
|
||||
<< " csrf-protection: "
|
||||
<< obj.getCsrfProtectionMode()
|
||||
<< " error-disclosure: "
|
||||
<< obj.getErrorDisclosureMode()
|
||||
<< " non-valid-http-methods: "
|
||||
<< obj.getNonValidHttpMethods()
|
||||
<< " open-redirect: "
|
||||
<< obj.getOpenRedirectMode();
|
||||
return os;
|
||||
}
|
||||
|
||||
class AppSecPracticeWebAttacks
|
||||
{
|
||||
public:
|
||||
void
|
||||
load(cereal::JSONInputArchive &archive_in)
|
||||
{
|
||||
dbgTrace(D_K8S_POLICY) << "Loading AppSec practice spec";
|
||||
parseAppsecJSONKey<AppSecWebAttackProtections>("protections", protections, archive_in);
|
||||
parseAppsecJSONKey<std::string>("minimum-confidence", minimum_confidence, archive_in, "critical");
|
||||
parseAppsecJSONKey<std::string>("override-mode", mode, archive_in, "Unset");
|
||||
parseAppsecJSONKey<int>("max-body-size-kb", max_body_size_kb, archive_in, 1000000);
|
||||
parseAppsecJSONKey<int>("max-header-size-bytes", max_header_size_bytes, archive_in, 102400);
|
||||
parseAppsecJSONKey<int>("max-object-depth", max_object_depth, archive_in, 40);
|
||||
parseAppsecJSONKey<int>("max-url-size-bytes", max_url_size_bytes, archive_in, 32768);
|
||||
}
|
||||
|
||||
int getMaxBodySizeKb() const { return max_body_size_kb; }
|
||||
int getMaxHeaderSizeBytes() const { return max_header_size_bytes; }
|
||||
int getMaxObjectDepth() const { return max_object_depth; }
|
||||
int getMaxUrlSizeBytes() const { return max_url_size_bytes; }
|
||||
const std::string & getMinimumConfidence() const { return minimum_confidence; }
|
||||
const AppSecWebAttackProtections & getprotections() const { return protections; }
|
||||
|
||||
const std::string &
|
||||
getMode(const std::string &default_mode = "Inactive") const
|
||||
{
|
||||
if (mode == "Unset" || (key_to_practices_val.find(mode) == key_to_practices_val.end())) {
|
||||
dbgError(D_K8S_POLICY) << "Couldn't find a value for key: " << mode << ". Returning " << default_mode;
|
||||
return default_mode;
|
||||
}
|
||||
return key_to_practices_val.at(mode);
|
||||
}
|
||||
|
||||
private:
|
||||
int max_body_size_kb;
|
||||
int max_header_size_bytes;
|
||||
int max_object_depth;
|
||||
int max_url_size_bytes;
|
||||
std::string minimum_confidence;
|
||||
std::string mode;
|
||||
AppSecWebAttackProtections protections;
|
||||
};
|
||||
|
||||
std::ostream &
|
||||
operator<<(std::ostream &os, const AppSecPracticeWebAttacks &obj)
|
||||
{
|
||||
os
|
||||
<< "mode: "
|
||||
<< obj.getMode()
|
||||
<< " max-body-size-kb: "
|
||||
<< obj.getMaxBodySizeKb()
|
||||
<< " max-header-size-bytes: "
|
||||
<< obj.getMaxHeaderSizeBytes()
|
||||
<< " max-object-depth: "
|
||||
<< obj.getMaxObjectDepth()
|
||||
<< " max-url-size-bytes: "
|
||||
<< obj.getMaxUrlSizeBytes()
|
||||
<< " minimum-confidence: "
|
||||
<< obj.getMinimumConfidence()
|
||||
<< " protections: "
|
||||
<< obj.getprotections();
|
||||
return os;
|
||||
}
|
||||
|
||||
class AppSecPracticeSnortSignatures
|
||||
{
|
||||
public:
|
||||
void
|
||||
load(cereal::JSONInputArchive &archive_in)
|
||||
{
|
||||
dbgTrace(D_K8S_POLICY) << "Loading AppSec Snort Signatures practice";
|
||||
parseAppsecJSONKey<std::string>("override-mode", override_mode, archive_in, "Inactive");
|
||||
parseAppsecJSONKey<std::vector<std::string>>("configmap", config_map, archive_in);
|
||||
}
|
||||
|
||||
const std::string & getOverrideMode() const { return override_mode; }
|
||||
|
||||
const std::vector<std::string> & getConfigMap() const { return config_map; }
|
||||
|
||||
private:
|
||||
std::string override_mode;
|
||||
std::vector<std::string> config_map;
|
||||
};
|
||||
|
||||
std::ostream &
|
||||
operator<<(std::ostream &os, const AppSecPracticeSnortSignatures &obj)
|
||||
{
|
||||
os
|
||||
<< "override mode: "
|
||||
<< obj.getOverrideMode()
|
||||
<< ". Config map: [" << std::endl
|
||||
<< makeSeparatedStr(obj.getConfigMap(), ",")
|
||||
<< std::endl << "]";
|
||||
return os;
|
||||
}
|
||||
|
||||
class AppSecPracticeOpenSchemaAPI
|
||||
{
|
||||
public:
|
||||
void
|
||||
load(cereal::JSONInputArchive &archive_in)
|
||||
{
|
||||
dbgTrace(D_K8S_POLICY) << "Loading AppSecPracticeOpenSchemaAPI practice";
|
||||
parseAppsecJSONKey<std::string>("override-mode", override_mode, archive_in, "Inactive");
|
||||
parseAppsecJSONKey<std::vector<std::string>>("configmap", config_map, archive_in);
|
||||
}
|
||||
|
||||
const std::string & getOverrideMode() const { return override_mode; }
|
||||
|
||||
const std::vector<std::string> & getConfigMap() const { return config_map; }
|
||||
|
||||
private:
|
||||
std::string override_mode;
|
||||
std::vector<std::string> config_map;
|
||||
};
|
||||
|
||||
std::ostream &
|
||||
operator<<(std::ostream &os, const AppSecPracticeOpenSchemaAPI &obj)
|
||||
{
|
||||
os
|
||||
<< "override mode: "
|
||||
<< obj.getOverrideMode()
|
||||
<< ". Config map: [" << std::endl
|
||||
<< makeSeparatedStr(obj.getConfigMap(), ",")
|
||||
<< std::endl << "]";
|
||||
return os;
|
||||
}
|
||||
|
||||
class AppSecPracticeSpec
|
||||
{
|
||||
public:
|
||||
void
|
||||
load(cereal::JSONInputArchive &archive_in)
|
||||
{
|
||||
dbgTrace(D_K8S_POLICY) << "Loading AppSec practice spec";
|
||||
parseAppsecJSONKey<AppSecPracticeOpenSchemaAPI>(
|
||||
"openapi-schema-validation",
|
||||
openapi_schema_validation,
|
||||
archive_in
|
||||
);
|
||||
parseAppsecJSONKey<AppSecPracticeSnortSignatures>("snort-signatures", snort_signatures, archive_in);
|
||||
parseAppsecJSONKey<AppSecPracticeWebAttacks>("web-attacks", web_attacks, archive_in);
|
||||
parseAppsecJSONKey<AppSecPracticeAntiBot>("anti-bot", anti_bot, archive_in);
|
||||
}
|
||||
|
||||
const AppSecPracticeOpenSchemaAPI & getOpenSchemaValidation() const { return openapi_schema_validation; }
|
||||
const AppSecPracticeSnortSignatures & getSnortSignatures() const { return snort_signatures; }
|
||||
const AppSecPracticeWebAttacks & getWebAttacks() const { return web_attacks; }
|
||||
const AppSecPracticeAntiBot & getAntiBot() const { return anti_bot; }
|
||||
|
||||
private:
|
||||
AppSecPracticeOpenSchemaAPI openapi_schema_validation;
|
||||
AppSecPracticeSnortSignatures snort_signatures;
|
||||
AppSecPracticeWebAttacks web_attacks;
|
||||
AppSecPracticeAntiBot anti_bot;
|
||||
};
|
||||
|
||||
std::ostream &
|
||||
operator<<(std::ostream &os, const AppSecPracticeSpec &obj)
|
||||
{
|
||||
os
|
||||
<< "Open Schema API:" << std::endl
|
||||
<< obj.getOpenSchemaValidation()
|
||||
<< std::endl << "Snort Signatures:" << std::endl
|
||||
<< obj.getOpenSchemaValidation()
|
||||
<< std::endl << "Web Attacks:" << std::endl
|
||||
<< obj.getWebAttacks()
|
||||
<< std::endl << "Web Bots:" << std::endl
|
||||
<< obj.getAntiBot();
|
||||
return os;
|
||||
}
|
||||
|
||||
class PracticeAdvancedConfig
|
||||
{
|
||||
public:
|
||||
PracticeAdvancedConfig(const AppSecPracticeSpec &parsed_appsec_spec)
|
||||
:
|
||||
http_header_max_size(parsed_appsec_spec.getWebAttacks().getMaxHeaderSizeBytes()),
|
||||
http_illegal_methods_allowed(0),
|
||||
http_request_body_max_size(parsed_appsec_spec.getWebAttacks().getMaxBodySizeKb()),
|
||||
json_max_object_depth(parsed_appsec_spec.getWebAttacks().getMaxObjectDepth()),
|
||||
url_max_size(parsed_appsec_spec.getWebAttacks().getMaxUrlSizeBytes())
|
||||
{}
|
||||
|
||||
void
|
||||
save(cereal::JSONOutputArchive &out_ar) const
|
||||
{
|
||||
out_ar(
|
||||
cereal::make_nvp("httpHeaderMaxSize", http_header_max_size),
|
||||
cereal::make_nvp("httpIllegalMethodsAllowed", http_illegal_methods_allowed),
|
||||
cereal::make_nvp("httpRequestBodyMaxSize", http_request_body_max_size),
|
||||
cereal::make_nvp("jsonMaxObjectDepth", json_max_object_depth),
|
||||
cereal::make_nvp("urlMaxSize", url_max_size)
|
||||
);
|
||||
}
|
||||
|
||||
void setIllegalMethodsAllowed(int val) { http_illegal_methods_allowed = val; };
|
||||
|
||||
private:
|
||||
int http_header_max_size;
|
||||
int http_illegal_methods_allowed;
|
||||
int http_request_body_max_size;
|
||||
int json_max_object_depth;
|
||||
int url_max_size;
|
||||
};
|
||||
|
||||
class TriggersInWaapSection
|
||||
{
|
||||
public:
|
||||
TriggersInWaapSection(const LogTriggerSection &log_section)
|
||||
:
|
||||
trigger_type("log"),
|
||||
id(log_section.getTriggerId()),
|
||||
name(log_section.getTriggerName()),
|
||||
log(log_section)
|
||||
{}
|
||||
|
||||
void
|
||||
save(cereal::JSONOutputArchive &out_ar) const
|
||||
{
|
||||
out_ar(
|
||||
cereal::make_nvp("$triggerType", trigger_type),
|
||||
cereal::make_nvp("id", id),
|
||||
cereal::make_nvp("name", name),
|
||||
cereal::make_nvp("log", log)
|
||||
);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string trigger_type;
|
||||
std::string id;
|
||||
std::string name;
|
||||
LogTriggerSection log;
|
||||
};
|
||||
|
||||
class AppSecOverride
|
||||
{
|
||||
public:
|
||||
AppSecOverride(const SourcesIdentifiers &parsed_trusted_sources)
|
||||
{
|
||||
std::string source_ident = parsed_trusted_sources.getSourceIdent();
|
||||
std::map<std::string, std::string> behavior = {{"httpSourceId", source_ident}};
|
||||
parsed_behavior.push_back(behavior);
|
||||
parsed_match = {{"operator", "BASIC"}, {"tag", "sourceip"}, {"value", "0.0.0.0/0"}};
|
||||
}
|
||||
|
||||
void
|
||||
save(cereal::JSONOutputArchive &out_ar) const
|
||||
{
|
||||
std::string parameter_type = "TrustedSource";
|
||||
out_ar(
|
||||
cereal::make_nvp("parsedBehavior", parsed_behavior),
|
||||
cereal::make_nvp("parsedMatch", parsed_match)
|
||||
);
|
||||
}
|
||||
private:
|
||||
std::vector<std::map<std::string, std::string>> parsed_behavior;
|
||||
std::map<std::string, std::string> parsed_match;
|
||||
};
|
||||
|
||||
class WebAppSection
|
||||
{
|
||||
public:
|
||||
WebAppSection(
|
||||
const std::string &_application_urls,
|
||||
const std::string &_asset_id,
|
||||
const std::string &_asset_name,
|
||||
const std::string &_rule_id,
|
||||
const std::string &_rule_name,
|
||||
const std::string &_practice_id,
|
||||
const std::string &_practice_name,
|
||||
const AppSecPracticeSpec &parsed_appsec_spec,
|
||||
const LogTriggerSection &parsed_log_trigger,
|
||||
const std::string &default_mode,
|
||||
const AppSecTrustedSources &parsed_trusted_sources)
|
||||
:
|
||||
application_urls(_application_urls),
|
||||
asset_id(_asset_id),
|
||||
asset_name(_asset_name),
|
||||
rule_id(_rule_id),
|
||||
rule_name(_rule_name),
|
||||
practice_id(_practice_id),
|
||||
practice_name(_practice_name),
|
||||
context("practiceId(" + practice_id +")"),
|
||||
web_attack_mitigation_severity(parsed_appsec_spec.getWebAttacks().getMinimumConfidence()),
|
||||
web_attack_mitigation_mode(parsed_appsec_spec.getWebAttacks().getMode(default_mode)),
|
||||
practice_advanced_config(parsed_appsec_spec),
|
||||
anti_bots(parsed_appsec_spec.getAntiBot()),
|
||||
trusted_sources({parsed_trusted_sources})
|
||||
{
|
||||
web_attack_mitigation = true;
|
||||
web_attack_mitigation_action =
|
||||
web_attack_mitigation_severity == "critical" ? "low" :
|
||||
web_attack_mitigation_severity == "high" ? "balanced" :
|
||||
web_attack_mitigation_severity == "medium" ? "high" :
|
||||
"Error";
|
||||
|
||||
triggers.push_back(TriggersInWaapSection(parsed_log_trigger));
|
||||
for (const SourcesIdentifiers &source_ident : parsed_trusted_sources.getSourcesIdentifiers()) {
|
||||
overrides.push_back(AppSecOverride(source_ident));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
serialize(cereal::JSONOutputArchive &out_ar) const
|
||||
{
|
||||
std::string disabled_str = "Disabled";
|
||||
std::string detect_str = "Detect";
|
||||
std::vector<std::string> empty_list;
|
||||
out_ar(
|
||||
cereal::make_nvp("context", context),
|
||||
cereal::make_nvp("webAttackMitigation", web_attack_mitigation),
|
||||
cereal::make_nvp("webAttackMitigationSeverity", web_attack_mitigation_severity),
|
||||
cereal::make_nvp("webAttackMitigationAction", web_attack_mitigation_action),
|
||||
cereal::make_nvp("webAttackMitigationMode", web_attack_mitigation_mode),
|
||||
cereal::make_nvp("practiceAdvancedConfig", practice_advanced_config),
|
||||
cereal::make_nvp("csrfProtection", disabled_str),
|
||||
cereal::make_nvp("openRedirect", disabled_str),
|
||||
cereal::make_nvp("errorDisclosure", disabled_str),
|
||||
cereal::make_nvp("practiceId", practice_id),
|
||||
cereal::make_nvp("practiceName", practice_name),
|
||||
cereal::make_nvp("assetId", asset_id),
|
||||
cereal::make_nvp("assetName", asset_name),
|
||||
cereal::make_nvp("ruleId", rule_id),
|
||||
cereal::make_nvp("ruleName", rule_name),
|
||||
cereal::make_nvp("triggers", triggers),
|
||||
cereal::make_nvp("applicationUrls", application_urls),
|
||||
cereal::make_nvp("overrides", overrides),
|
||||
cereal::make_nvp("trustedSources", trusted_sources),
|
||||
cereal::make_nvp("waapParameters", empty_list),
|
||||
cereal::make_nvp("botProtection", false),
|
||||
cereal::make_nvp("antiBot", anti_bots),
|
||||
cereal::make_nvp("botProtection_v2", detect_str)
|
||||
);
|
||||
}
|
||||
|
||||
const std::string & getPracticeId() const { return practice_id; }
|
||||
|
||||
bool
|
||||
operator<(const WebAppSection &other) const
|
||||
{
|
||||
return getPracticeId() < other.getPracticeId();
|
||||
}
|
||||
|
||||
private:
|
||||
std::string application_urls;
|
||||
std::string asset_id;
|
||||
std::string asset_name;
|
||||
std::string rule_id;
|
||||
std::string rule_name;
|
||||
std::string practice_id;
|
||||
std::string practice_name;
|
||||
std::string context;
|
||||
std::string web_attack_mitigation_action;
|
||||
std::string web_attack_mitigation_severity;
|
||||
std::string web_attack_mitigation_mode;
|
||||
bool web_attack_mitigation;
|
||||
std::vector<TriggersInWaapSection> triggers;
|
||||
PracticeAdvancedConfig practice_advanced_config;
|
||||
AppSecPracticeAntiBot anti_bots;
|
||||
std::vector<AppSecTrustedSources> trusted_sources;
|
||||
std::vector<AppSecOverride> overrides;
|
||||
};
|
||||
|
||||
class WebAPISection
|
||||
{
|
||||
public:
|
||||
WebAPISection(
|
||||
const std::string &_application_urls,
|
||||
const std::string &_asset_id,
|
||||
const std::string &_asset_name,
|
||||
const std::string &_rule_id,
|
||||
const std::string &_rule_name,
|
||||
const std::string &_practice_id,
|
||||
const std::string &_practice_name,
|
||||
const std::string &_web_attack_mitigation_action,
|
||||
const std::string &_web_attack_mitigation_severity,
|
||||
const std::string &_web_attack_mitigation_mode,
|
||||
bool _web_attack_mitigation,
|
||||
const AppSecPracticeSpec &parsed_appsec_spec)
|
||||
:
|
||||
application_urls(_application_urls),
|
||||
asset_id(_asset_id),
|
||||
asset_name(_asset_name),
|
||||
rule_id(_rule_id),
|
||||
rule_name(_rule_name),
|
||||
practice_id(_practice_id),
|
||||
practice_name(_practice_name),
|
||||
context("practiceId(" + practice_id +")"),
|
||||
web_attack_mitigation_action(_web_attack_mitigation_action),
|
||||
web_attack_mitigation_severity(_web_attack_mitigation_severity),
|
||||
web_attack_mitigation_mode(_web_attack_mitigation_mode),
|
||||
web_attack_mitigation(_web_attack_mitigation),
|
||||
practice_advanced_config(parsed_appsec_spec)
|
||||
{}
|
||||
|
||||
void
|
||||
serialize(cereal::JSONOutputArchive &out_ar) const
|
||||
{
|
||||
std::string disabled_str = "Disabled";
|
||||
std::vector<std::string> empty_list;
|
||||
out_ar(
|
||||
cereal::make_nvp("application_urls", application_urls),
|
||||
cereal::make_nvp("asset_id", asset_id),
|
||||
cereal::make_nvp("asset_name", asset_name),
|
||||
cereal::make_nvp("context", context),
|
||||
cereal::make_nvp("practiceAdvancedConfig", practice_advanced_config),
|
||||
cereal::make_nvp("practice_id", practice_id),
|
||||
cereal::make_nvp("practice_name", practice_name),
|
||||
cereal::make_nvp("ruleId", rule_id),
|
||||
cereal::make_nvp("ruleName", rule_name),
|
||||
cereal::make_nvp("schemaValidation", false),
|
||||
cereal::make_nvp("schemaValidation_v2", disabled_str),
|
||||
cereal::make_nvp("web_attack_mitigation", web_attack_mitigation),
|
||||
cereal::make_nvp("web_attack_mitigation_action", web_attack_mitigation_action),
|
||||
cereal::make_nvp("web_attack_mitigation_severity", web_attack_mitigation_severity),
|
||||
cereal::make_nvp("web_attack_mitigation_mode", web_attack_mitigation_mode),
|
||||
cereal::make_nvp("oas", empty_list),
|
||||
cereal::make_nvp("trustedSources", empty_list),
|
||||
cereal::make_nvp("triggers", empty_list),
|
||||
cereal::make_nvp("waapParameters", empty_list),
|
||||
cereal::make_nvp("overrides", empty_list)
|
||||
);
|
||||
}
|
||||
|
||||
const std::string & getPracticeId() const { return practice_id; }
|
||||
|
||||
private:
|
||||
std::string application_urls;
|
||||
std::string asset_id;
|
||||
std::string asset_name;
|
||||
std::string rule_id;
|
||||
std::string rule_name;
|
||||
std::string practice_id;
|
||||
std::string practice_name;
|
||||
std::string context;
|
||||
std::string web_attack_mitigation_action;
|
||||
std::string web_attack_mitigation_severity;
|
||||
std::string web_attack_mitigation_mode;
|
||||
bool web_attack_mitigation;
|
||||
PracticeAdvancedConfig practice_advanced_config;
|
||||
};
|
||||
|
||||
class AppSecRulebase
|
||||
{
|
||||
public:
|
||||
AppSecRulebase(
|
||||
std::vector<WebAppSection> _webApplicationPractices,
|
||||
std::vector<WebAPISection> _webAPIPractices)
|
||||
:
|
||||
webApplicationPractices(_webApplicationPractices),
|
||||
webAPIPractices(_webAPIPractices) {}
|
||||
|
||||
void
|
||||
serialize(cereal::JSONOutputArchive &out_ar) const
|
||||
{
|
||||
out_ar(
|
||||
cereal::make_nvp("WebAPISecurity", webAPIPractices),
|
||||
cereal::make_nvp("WebApplicationSecurity", webApplicationPractices)
|
||||
);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<WebAppSection> webApplicationPractices;
|
||||
std::vector<WebAPISection> webAPIPractices;
|
||||
};
|
||||
|
||||
class AppSecWrapper
|
||||
{
|
||||
public:
|
||||
AppSecWrapper(const AppSecRulebase &_app_sec)
|
||||
:
|
||||
app_sec_rulebase(_app_sec)
|
||||
{}
|
||||
|
||||
void
|
||||
serialize(cereal::JSONOutputArchive &out_ar) const
|
||||
{
|
||||
out_ar(cereal::make_nvp("WAAP", app_sec_rulebase));
|
||||
}
|
||||
|
||||
private:
|
||||
AppSecRulebase app_sec_rulebase;
|
||||
};
|
||||
|
||||
|
||||
class ParsedRule
|
||||
{
|
||||
public:
|
||||
void
|
||||
load(cereal::JSONInputArchive &archive_in)
|
||||
{
|
||||
dbgTrace(D_K8S_POLICY) << "Loading AppSec ParsedRule";
|
||||
parseAppsecJSONKey<std::vector<std::string>>("exceptions", exceptions, archive_in);
|
||||
parseAppsecJSONKey<std::vector<std::string>>("triggers", log_triggers, archive_in);
|
||||
parseAppsecJSONKey<std::vector<std::string>>("practices", practices, archive_in);
|
||||
parseAppsecJSONKey<std::string>("mode", mode, archive_in);
|
||||
parseAppsecJSONKey<std::string>("custom-response", custom_response, archive_in);
|
||||
parseAppsecJSONKey<std::string>("source-identifiers", source_identifiers, archive_in);
|
||||
parseAppsecJSONKey<std::string>("trusted-sources", trusted_sources, archive_in);
|
||||
try {
|
||||
archive_in(cereal::make_nvp("host", host));
|
||||
} catch (const cereal::Exception &e)
|
||||
{} // The default ParsedRule does not hold a host, so no error handling
|
||||
}
|
||||
|
||||
const std::vector<std::string> & getExceptions() const { return exceptions; }
|
||||
|
||||
const std::vector<std::string> & getLogTriggers() const { return log_triggers; }
|
||||
|
||||
const std::vector<std::string> & getPractices() const { return practices; }
|
||||
|
||||
const std::string & getHost() const { return host; }
|
||||
|
||||
const std::string & getMode() const { return mode; }
|
||||
|
||||
void setMode(const std::string &_mode) { mode = _mode; };
|
||||
|
||||
const std::string & getCustomResponse() const { return custom_response; }
|
||||
|
||||
const std::string & getSourceIdentifiers() const { return source_identifiers; }
|
||||
|
||||
const std::string & getTrustedSources() const { return trusted_sources; }
|
||||
|
||||
private:
|
||||
std::vector<std::string> exceptions;
|
||||
std::vector<std::string> log_triggers;
|
||||
std::vector<std::string> practices;
|
||||
std::string host;
|
||||
std::string mode;
|
||||
std::string custom_response;
|
||||
std::string source_identifiers;
|
||||
std::string trusted_sources;
|
||||
};
|
||||
|
||||
std::ostream &
|
||||
operator<<(std::ostream &os, const ParsedRule &obj)
|
||||
{
|
||||
os
|
||||
<< "host: "
|
||||
<< obj.getHost()
|
||||
<< std::endl << "log trigger: "
|
||||
<< makeSeparatedStr(obj.getLogTriggers(), ",")
|
||||
<< std::endl << "mode: "
|
||||
<< obj.getMode()
|
||||
<< std::endl << "practices: "
|
||||
<< makeSeparatedStr(obj.getPractices(), ",")
|
||||
<< std::endl << "web responce: "
|
||||
<< obj.getCustomResponse()
|
||||
<< std::endl << " Exceptions: [" << std::endl
|
||||
<< makeSeparatedStr(obj.getExceptions(), ",")
|
||||
<< std::endl << "]";
|
||||
return os;
|
||||
}
|
||||
|
||||
class AppsecPolicySpec : Singleton::Consume<I_Environment>
|
||||
{
|
||||
public:
|
||||
void
|
||||
load(cereal::JSONInputArchive &archive_in)
|
||||
{
|
||||
dbgTrace(D_K8S_POLICY) << "Loading AppSec policy spec";
|
||||
parseAppsecJSONKey<ParsedRule>("default", default_rule, archive_in);
|
||||
auto default_mode_annot =
|
||||
Singleton::Consume<I_Environment>::by<AppsecPolicySpec>()->get<std::string>("default mode annotation");
|
||||
if (default_mode_annot.ok() && !default_mode_annot.unpack().empty() && default_rule.getMode().empty()) {
|
||||
default_rule.setMode(default_mode_annot.unpack());
|
||||
}
|
||||
parseAppsecJSONKey<std::vector<ParsedRule>>("specific-rules", specific_rules, archive_in);
|
||||
}
|
||||
|
||||
const ParsedRule & getDefaultRule() const { return default_rule; }
|
||||
|
||||
const std::vector<ParsedRule> & getSpecificRules() const { return specific_rules; }
|
||||
|
||||
private:
|
||||
ParsedRule default_rule;
|
||||
std::vector<ParsedRule> specific_rules;
|
||||
};
|
||||
|
||||
std::ostream &
|
||||
operator<<(std::ostream &os, const AppsecPolicySpec &obj)
|
||||
{
|
||||
os
|
||||
<< "Default Rule: "
|
||||
<< obj.getDefaultRule()
|
||||
<< std::endl <<"Specific Rules: [" << std::endl
|
||||
<< makeSeparatedStr(obj.getSpecificRules(), ",")
|
||||
<< std::endl << "]";
|
||||
return os;
|
||||
}
|
||||
|
||||
#endif // __APPSEC_PRACTICE_SECTION_H__
|
||||
@@ -0,0 +1,313 @@
|
||||
// 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 __EXCEPTPIONS_SECTION_H__
|
||||
#define __EXCEPTPIONS_SECTION_H__
|
||||
|
||||
#include <string>
|
||||
#include <cereal/archives/json.hpp>
|
||||
#include <boost/uuid/uuid.hpp>
|
||||
#include <boost/uuid/uuid_generators.hpp>
|
||||
#include <boost/uuid/uuid_io.hpp>
|
||||
|
||||
#include "config.h"
|
||||
#include "debug.h"
|
||||
#include "rest.h"
|
||||
#include "k8s_policy_common.h"
|
||||
|
||||
USE_DEBUG_FLAG(D_K8S_POLICY);
|
||||
|
||||
class AppsecExceptionSpec
|
||||
{
|
||||
public:
|
||||
void
|
||||
load(cereal::JSONInputArchive &archive_in)
|
||||
{
|
||||
dbgTrace(D_K8S_POLICY) << "Loading AppSec exception spec";
|
||||
parseAppsecJSONKey<std::string>("action", action, archive_in);
|
||||
parseAppsecJSONKey<std::vector<std::string>>("countryCode", country_code, archive_in);
|
||||
parseAppsecJSONKey<std::vector<std::string>>("countryName", country_name, archive_in);
|
||||
parseAppsecJSONKey<std::vector<std::string>>("hostName", host_name, archive_in);
|
||||
parseAppsecJSONKey<std::vector<std::string>>("paramName", param_name, archive_in);
|
||||
parseAppsecJSONKey<std::vector<std::string>>("paramValue", param_value, archive_in);
|
||||
parseAppsecJSONKey<std::vector<std::string>>("protectionName", protection_name, archive_in);
|
||||
parseAppsecJSONKey<std::vector<std::string>>("sourceIdentifier", source_identifier, archive_in);
|
||||
parseAppsecJSONKey<std::vector<std::string>>("sourceIp", source_ip, archive_in);
|
||||
parseAppsecJSONKey<std::vector<std::string>>("url", url, archive_in);
|
||||
}
|
||||
|
||||
const std::string & getAction() const { return action; }
|
||||
const std::vector<std::string> & getCountryCode() const { return country_code; }
|
||||
const std::vector<std::string> & getCountryName() const { return country_name; }
|
||||
const std::vector<std::string> & getHostName() const { return host_name; }
|
||||
const std::vector<std::string> & getParamName() const { return param_name; }
|
||||
const std::vector<std::string> & getParamValue() const { return param_value; }
|
||||
const std::vector<std::string> & getProtectionName() const { return protection_name; }
|
||||
const std::vector<std::string> & getSourceIdentifier() const { return source_identifier; }
|
||||
const std::vector<std::string> & getSourceIp() const { return source_ip; }
|
||||
const std::vector<std::string> & getUrl() const { return url; }
|
||||
|
||||
private:
|
||||
std::string action;
|
||||
std::vector<std::string> country_code;
|
||||
std::vector<std::string> country_name;
|
||||
std::vector<std::string> host_name;
|
||||
std::vector<std::string> param_name;
|
||||
std::vector<std::string> param_value;
|
||||
std::vector<std::string> protection_name;
|
||||
std::vector<std::string> source_identifier;
|
||||
std::vector<std::string> source_ip;
|
||||
std::vector<std::string> url;
|
||||
};
|
||||
|
||||
std::ostream &
|
||||
operator<<(std::ostream &os, const AppsecExceptionSpec &obj)
|
||||
{
|
||||
os
|
||||
<< "action: "
|
||||
<< makeSeparatedStr(obj.getAction(), ",")
|
||||
<< "countryCode: "
|
||||
<< makeSeparatedStr(obj.getCountryCode(), ",")
|
||||
<< "countryName: "
|
||||
<< makeSeparatedStr(obj.getCountryName(), ",")
|
||||
<< "hostName: "
|
||||
<< makeSeparatedStr(obj.getHostName(), ",")
|
||||
<< "paramName: "
|
||||
<< makeSeparatedStr(obj.getParamName(), ",")
|
||||
<< "paramValue: "
|
||||
<< makeSeparatedStr(obj.getParamValue(), ",")
|
||||
<< "protectionName: "
|
||||
<< makeSeparatedStr(obj.getProtectionName(), ",")
|
||||
<< "sourceIdentifier: "
|
||||
<< makeSeparatedStr(obj.getSourceIdentifier(), ",")
|
||||
<< "sourceIp: "
|
||||
<< makeSeparatedStr(obj.getSourceIp(), ",")
|
||||
<< "url: "
|
||||
<< makeSeparatedStr(obj.getUrl(), ",");
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
class ExceptionMatch
|
||||
{
|
||||
public:
|
||||
ExceptionMatch(const AppsecExceptionSpec &parsed_exception)
|
||||
:
|
||||
match_type(MatchType::Operator),
|
||||
op("and")
|
||||
{
|
||||
if (!parsed_exception.getCountryCode().empty()) {
|
||||
items.push_back(ExceptionMatch("countryCode", parsed_exception.getCountryCode()));
|
||||
}
|
||||
if (!parsed_exception.getCountryName().empty()) {
|
||||
items.push_back(ExceptionMatch("countryName", parsed_exception.getCountryName()));
|
||||
}
|
||||
if (!parsed_exception.getHostName().empty()) {
|
||||
items.push_back(ExceptionMatch("hostName", parsed_exception.getHostName()));
|
||||
}
|
||||
if (!parsed_exception.getParamName().empty()) {
|
||||
items.push_back(ExceptionMatch("paramName", parsed_exception.getParamName()));
|
||||
}
|
||||
if (!parsed_exception.getParamValue().empty()) {
|
||||
items.push_back(ExceptionMatch("paramValue", parsed_exception.getParamValue()));
|
||||
}
|
||||
if (!parsed_exception.getProtectionName().empty()) {
|
||||
items.push_back(ExceptionMatch("protectionName", parsed_exception.getProtectionName()));
|
||||
}
|
||||
if (!parsed_exception.getSourceIdentifier().empty()) {
|
||||
items.push_back(ExceptionMatch("sourceIdentifier", parsed_exception.getSourceIdentifier()));
|
||||
}
|
||||
if (!parsed_exception.getSourceIp().empty()) {
|
||||
items.push_back(ExceptionMatch("sourceIp", parsed_exception.getSourceIp()));
|
||||
}
|
||||
if (!parsed_exception.getUrl().empty()) {
|
||||
items.push_back(ExceptionMatch("url", parsed_exception.getUrl()));
|
||||
}
|
||||
}
|
||||
|
||||
ExceptionMatch(const std::string &_key, const std::vector<std::string> &_value)
|
||||
:
|
||||
match_type(MatchType::Condition),
|
||||
key(_key),
|
||||
op("in"),
|
||||
value(_value)
|
||||
{}
|
||||
|
||||
void
|
||||
serialize(cereal::JSONOutputArchive &out_ar) const
|
||||
{
|
||||
switch (match_type) {
|
||||
case (MatchType::Condition): {
|
||||
std::string type_str = "condition";
|
||||
out_ar(
|
||||
cereal::make_nvp("key", key),
|
||||
cereal::make_nvp("op", op),
|
||||
cereal::make_nvp("type", type_str),
|
||||
cereal::make_nvp("value", value)
|
||||
);
|
||||
break;
|
||||
}
|
||||
case (MatchType::Operator): {
|
||||
std::string type_str = "operator";
|
||||
out_ar(
|
||||
cereal::make_nvp("op", op),
|
||||
cereal::make_nvp("type", type_str),
|
||||
cereal::make_nvp("items", items)
|
||||
);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
dbgError(D_K8S_POLICY) << "No match for exception match type: " << static_cast<int>(match_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
MatchType match_type;
|
||||
std::string key;
|
||||
std::string op;
|
||||
std::vector<std::string> value;
|
||||
std::vector<ExceptionMatch> items;
|
||||
};
|
||||
|
||||
class ExceptionBehavior
|
||||
{
|
||||
public:
|
||||
ExceptionBehavior(
|
||||
const std::string &_key,
|
||||
const std::string &_value)
|
||||
:
|
||||
key(_key),
|
||||
value(_value)
|
||||
{
|
||||
try {
|
||||
id = to_string(boost::uuids::random_generator()());
|
||||
} catch (const boost::uuids::entropy_error &e) {
|
||||
dbgWarning(D_K8S_POLICY) << "Failed to generate exception behavior UUID. Error: " << e.what();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
serialize(cereal::JSONOutputArchive &out_ar) const
|
||||
{
|
||||
out_ar(
|
||||
cereal::make_nvp("key", key),
|
||||
cereal::make_nvp("value", value),
|
||||
cereal::make_nvp("id", id)
|
||||
);
|
||||
}
|
||||
|
||||
const std::string getBehaviorId() const { return id; }
|
||||
|
||||
private:
|
||||
std::string key;
|
||||
std::string id;
|
||||
std::string value;
|
||||
};
|
||||
|
||||
class InnerException
|
||||
{
|
||||
public:
|
||||
InnerException(
|
||||
ExceptionBehavior _behavior,
|
||||
ExceptionMatch _match)
|
||||
:
|
||||
behavior(_behavior),
|
||||
match(_match) {}
|
||||
|
||||
void
|
||||
serialize(cereal::JSONOutputArchive &out_ar) const
|
||||
{
|
||||
out_ar(
|
||||
cereal::make_nvp("behavior", behavior),
|
||||
cereal::make_nvp("match", match)
|
||||
);
|
||||
}
|
||||
|
||||
const std::string getBehaviorId() const { return behavior.getBehaviorId(); }
|
||||
|
||||
bool
|
||||
operator<(const InnerException &other) const
|
||||
{
|
||||
return getBehaviorId() < other.getBehaviorId();
|
||||
}
|
||||
|
||||
private:
|
||||
ExceptionBehavior behavior;
|
||||
ExceptionMatch match;
|
||||
};
|
||||
|
||||
class ExceptionsRulebase
|
||||
{
|
||||
public:
|
||||
ExceptionsRulebase(
|
||||
std::vector<InnerException> _exceptions)
|
||||
:
|
||||
exceptions(_exceptions)
|
||||
{
|
||||
std::string context_id_str = "";
|
||||
for (const InnerException exception : exceptions) {
|
||||
std::string curr_id = "parameterId(" + exception.getBehaviorId() + "), ";
|
||||
context_id_str += curr_id;
|
||||
}
|
||||
context_id_str = context_id_str.substr(0, context_id_str.size() - 2);
|
||||
context = "Any(" + context_id_str + ")";
|
||||
}
|
||||
|
||||
void
|
||||
serialize(cereal::JSONOutputArchive &out_ar) const
|
||||
{
|
||||
out_ar(
|
||||
cereal::make_nvp("context", context),
|
||||
cereal::make_nvp("exceptions", exceptions)
|
||||
);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string context;
|
||||
std::vector<InnerException> exceptions;
|
||||
};
|
||||
|
||||
class ExceptionsWrapper
|
||||
{
|
||||
public:
|
||||
class Exception
|
||||
{
|
||||
public:
|
||||
Exception(const std::vector<ExceptionsRulebase> &_exception) : exception(_exception) {}
|
||||
|
||||
void
|
||||
serialize(cereal::JSONOutputArchive &out_ar) const
|
||||
{
|
||||
out_ar(cereal::make_nvp("exception", exception));
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<ExceptionsRulebase> exception;
|
||||
};
|
||||
ExceptionsWrapper(const std::vector<ExceptionsRulebase> &_exception) : exception_rulebase(Exception(_exception))
|
||||
{}
|
||||
|
||||
void
|
||||
serialize(cereal::JSONOutputArchive &out_ar) const
|
||||
{
|
||||
out_ar(
|
||||
cereal::make_nvp("rulebase", exception_rulebase)
|
||||
);
|
||||
}
|
||||
|
||||
private:
|
||||
Exception exception_rulebase;
|
||||
};
|
||||
|
||||
#endif // __EXCEPTPIONS_SECTION_H__
|
||||
@@ -0,0 +1,224 @@
|
||||
// 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 __INGRESS_DATA_H__
|
||||
#define __INGRESS_DATA_H__
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#include "config.h"
|
||||
#include "debug.h"
|
||||
#include "rest.h"
|
||||
#include "cereal/archives/json.hpp"
|
||||
|
||||
USE_DEBUG_FLAG(D_K8S_POLICY);
|
||||
|
||||
class IngressMetadata
|
||||
{
|
||||
public:
|
||||
void
|
||||
load(cereal::JSONInputArchive &archive_in)
|
||||
{
|
||||
dbgTrace(D_K8S_POLICY) << "IngressMetadata load";
|
||||
parseAppsecJSONKey<std::string>("name", name, archive_in);
|
||||
parseAppsecJSONKey<std::string>("resourceVersion", resourceVersion, archive_in);
|
||||
parseAppsecJSONKey<std::string>("namespace", namespace_name, archive_in);
|
||||
parseAppsecJSONKey<std::map<std::string, std::string>>("annotations", annotations, archive_in);
|
||||
}
|
||||
|
||||
const std::string & getName() const { return name; }
|
||||
const std::string & getResourceVersion() const { return resourceVersion; }
|
||||
const std::string & getNamespace() const { return namespace_name; }
|
||||
const std::map<std::string, std::string> & getAnnotations() const { return annotations; }
|
||||
|
||||
private:
|
||||
std::string name;
|
||||
std::string resourceVersion;
|
||||
std::string namespace_name;
|
||||
std::map<std::string, std::string> annotations;
|
||||
};
|
||||
|
||||
class IngressRulePath
|
||||
{
|
||||
public:
|
||||
void
|
||||
load(cereal::JSONInputArchive &archive_in)
|
||||
{
|
||||
dbgTrace(D_K8S_POLICY) << "Loading ingress defined rule path";
|
||||
parseAppsecJSONKey<std::string>("path", path, archive_in);
|
||||
}
|
||||
|
||||
const std::string & getPath() const { return path; }
|
||||
|
||||
private:
|
||||
std::string path;
|
||||
};
|
||||
|
||||
std::ostream &
|
||||
operator<<(std::ostream &os, const IngressRulePath &obj)
|
||||
{
|
||||
os << obj.getPath();
|
||||
return os;
|
||||
}
|
||||
|
||||
class IngressRulePathsWrapper
|
||||
{
|
||||
public:
|
||||
void
|
||||
load(cereal::JSONInputArchive &archive_in)
|
||||
{
|
||||
dbgTrace(D_K8S_POLICY) << "Loading ingress defined rule path wrapper";
|
||||
parseAppsecJSONKey<std::vector<IngressRulePath>>("paths", paths, archive_in);
|
||||
}
|
||||
|
||||
const std::vector<IngressRulePath> & getRulePaths() const { return paths; }
|
||||
|
||||
private:
|
||||
std::vector<IngressRulePath> paths;
|
||||
};
|
||||
|
||||
class IngressDefinedRule
|
||||
{
|
||||
public:
|
||||
void
|
||||
load(cereal::JSONInputArchive &archive_in)
|
||||
{
|
||||
dbgTrace(D_K8S_POLICY) << "Loading ingress defined rule";
|
||||
parseAppsecJSONKey<std::string>("host", host, archive_in);
|
||||
parseAppsecJSONKey<IngressRulePathsWrapper>("http", paths_wrapper, archive_in);
|
||||
}
|
||||
|
||||
const std::string & getHost() const { return host; }
|
||||
const IngressRulePathsWrapper & getPathsWrapper() const { return paths_wrapper; }
|
||||
|
||||
private:
|
||||
std::string host;
|
||||
IngressRulePathsWrapper paths_wrapper;
|
||||
};
|
||||
|
||||
std::ostream &
|
||||
operator<<(std::ostream &os, const IngressDefinedRule &obj)
|
||||
{
|
||||
os
|
||||
<< "host: "
|
||||
<< obj.getHost()
|
||||
<< ", paths: [" << std::endl
|
||||
<< makeSeparatedStr(obj.getPathsWrapper().getRulePaths(), ",")
|
||||
<< std::endl << "]";
|
||||
return os;
|
||||
}
|
||||
|
||||
class DefaultBackend
|
||||
{
|
||||
public:
|
||||
void
|
||||
load(cereal::JSONInputArchive &)
|
||||
{
|
||||
dbgTrace(D_K8S_POLICY) << "Loading Default Backend";
|
||||
is_exists = true;
|
||||
}
|
||||
|
||||
bool isExists() const { return is_exists; }
|
||||
|
||||
private:
|
||||
bool is_exists = false;
|
||||
};
|
||||
|
||||
class IngressSpec
|
||||
{
|
||||
public:
|
||||
void
|
||||
load(cereal::JSONInputArchive &archive_in)
|
||||
{
|
||||
dbgTrace(D_K8S_POLICY) << "Loading single ingress spec";
|
||||
parseAppsecJSONKey<std::string>("ingressClassName", ingress_class_name, archive_in);
|
||||
parseAppsecJSONKey<std::vector<IngressDefinedRule>>("rules", rules, archive_in);
|
||||
parseAppsecJSONKey<DefaultBackend>("defaultBackend", default_backend, archive_in);
|
||||
}
|
||||
|
||||
const std::string & getIngressClassName() const { return ingress_class_name; }
|
||||
const std::vector<IngressDefinedRule> & getRules() const { return rules; }
|
||||
bool isDefaultBackendExists() const { return default_backend.isExists(); }
|
||||
|
||||
private:
|
||||
std::string ingress_class_name;
|
||||
std::vector<IngressDefinedRule> rules;
|
||||
DefaultBackend default_backend;
|
||||
};
|
||||
|
||||
std::ostream &
|
||||
operator<<(std::ostream &os, const IngressSpec &obj)
|
||||
{
|
||||
os
|
||||
<< "Ingress Spec - ingressClassName: "
|
||||
<< obj.getIngressClassName()
|
||||
<< ", rules: [" << std::endl
|
||||
<< makeSeparatedStr(obj.getRules(), ",")
|
||||
<< std::endl << "]";
|
||||
return os;
|
||||
}
|
||||
|
||||
class SingleIngressData
|
||||
{
|
||||
public:
|
||||
void
|
||||
load(cereal::JSONInputArchive &archive_in)
|
||||
{
|
||||
dbgTrace(D_K8S_POLICY) << "Loading single ingress data";
|
||||
parseAppsecJSONKey<IngressMetadata>("metadata", metadata, archive_in);
|
||||
parseAppsecJSONKey<IngressSpec>("spec", spec, archive_in);
|
||||
}
|
||||
|
||||
const IngressMetadata & getMetadata() const { return metadata; }
|
||||
const IngressSpec & getSpec() const { return spec; }
|
||||
|
||||
private:
|
||||
IngressMetadata metadata;
|
||||
IngressSpec spec;
|
||||
};
|
||||
|
||||
|
||||
class IngressData : public ClientRest
|
||||
{
|
||||
public:
|
||||
bool
|
||||
loadJson(const std::string &json)
|
||||
{
|
||||
std::string modified_json = json;
|
||||
modified_json.pop_back();
|
||||
std::stringstream in;
|
||||
in.str(modified_json);
|
||||
dbgTrace(D_K8S_POLICY) << "Loading ingress data";
|
||||
try {
|
||||
cereal::JSONInputArchive in_ar(in);
|
||||
in_ar(
|
||||
cereal::make_nvp("apiVersion", apiVersion),
|
||||
cereal::make_nvp("items", items)
|
||||
);
|
||||
} catch (cereal::Exception &e) {
|
||||
dbgError(D_K8S_POLICY) << "Failed to load ingress data JSON. Error: " << e.what();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::string & getapiVersion() const { return apiVersion; }
|
||||
const std::vector<SingleIngressData> & getItems() const { return items; }
|
||||
|
||||
private:
|
||||
std::string apiVersion;
|
||||
std::vector<SingleIngressData> items;
|
||||
};
|
||||
|
||||
#endif // __INGRESS_DATA_H__
|
||||
@@ -0,0 +1,103 @@
|
||||
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef __K8S_POLICY_COMMON_H__
|
||||
#define __K8S_POLICY_COMMON_H__
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <cereal/archives/json.hpp>
|
||||
|
||||
#include "config.h"
|
||||
#include "debug.h"
|
||||
#include "rest.h"
|
||||
|
||||
USE_DEBUG_FLAG(D_K8S_POLICY);
|
||||
|
||||
enum class PracticeType { WebApplication, WebAPI };
|
||||
enum class TriggerType { Log, WebUserResponse };
|
||||
enum class MatchType { Condition, Operator };
|
||||
|
||||
static const std::unordered_map<std::string, MatchType> string_to_match_type = {
|
||||
{ "condition", MatchType::Condition },
|
||||
{ "operator", MatchType::Operator }
|
||||
};
|
||||
|
||||
static const std::unordered_map<std::string, PracticeType> string_to_practice_type = {
|
||||
{ "WebApplication", PracticeType::WebApplication },
|
||||
{ "WebAPI", PracticeType::WebAPI }
|
||||
};
|
||||
|
||||
static const std::unordered_map<std::string, TriggerType> string_to_trigger_type = {
|
||||
{ "log", TriggerType::Log },
|
||||
{ "WebUserResponse", TriggerType::WebUserResponse }
|
||||
};
|
||||
|
||||
static const std::unordered_map<std::string, std::string> key_to_practices_val = {
|
||||
{ "prevent-learn", "Prevent"},
|
||||
{ "detect-learn", "Detect"},
|
||||
{ "prevent", "Prevent"},
|
||||
{ "detect", "Detect"},
|
||||
{ "inactive", "Inactive"}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
parseAppsecJSONKey(
|
||||
const std::string &key_name,
|
||||
T &value,
|
||||
cereal::JSONInputArchive &archive_in,
|
||||
const T &default_value = T())
|
||||
{
|
||||
try {
|
||||
archive_in(cereal::make_nvp(key_name, value));
|
||||
} catch (const cereal::Exception &e) {
|
||||
archive_in.setNextName(nullptr);
|
||||
value = default_value;
|
||||
dbgDebug(D_K8S_POLICY)
|
||||
<< "Could not parse the required key. Key: "
|
||||
<< key_name
|
||||
<< ", Error: "
|
||||
<< e.what();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
class AppsecSpecParser : public ClientRest
|
||||
{
|
||||
public:
|
||||
bool
|
||||
loadJson(const std::string &json)
|
||||
{
|
||||
std::string modified_json = json;
|
||||
modified_json.pop_back();
|
||||
std::stringstream ss;
|
||||
ss.str(modified_json);
|
||||
try {
|
||||
cereal::JSONInputArchive in_ar(ss);
|
||||
in_ar(cereal::make_nvp("spec", spec));
|
||||
} catch (cereal::Exception &e) {
|
||||
dbgError(D_K8S_POLICY) << "Failed to load spec JSON. Error: " << e.what();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const T & getSpec() const { return spec; }
|
||||
|
||||
private:
|
||||
T spec;
|
||||
};
|
||||
|
||||
#endif // __K8S_POLICY_COMMON_H__
|
||||
@@ -0,0 +1,391 @@
|
||||
// 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 __RULES_CONFIG_SECTION_H__
|
||||
#define __RULES_CONFIG_SECTION_H__
|
||||
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <cereal/archives/json.hpp>
|
||||
#include <boost/uuid/uuid.hpp>
|
||||
#include <boost/uuid/uuid_generators.hpp>
|
||||
#include <boost/uuid/uuid_io.hpp>
|
||||
|
||||
#include "config.h"
|
||||
#include "debug.h"
|
||||
#include "k8s_policy_common.h"
|
||||
|
||||
USE_DEBUG_FLAG(D_K8S_POLICY);
|
||||
|
||||
class AssetUrlParser
|
||||
{
|
||||
public:
|
||||
std::string query_string, asset_uri, protocol, asset_url, port;
|
||||
|
||||
AssetUrlParser()
|
||||
{}
|
||||
|
||||
AssetUrlParser(const std::string &asset)
|
||||
{
|
||||
parse(asset);
|
||||
}
|
||||
|
||||
private:
|
||||
static AssetUrlParser
|
||||
parse(const std::string &uri)
|
||||
{
|
||||
AssetUrlParser result;
|
||||
|
||||
using iterator_t = std::string::const_iterator;
|
||||
|
||||
if (uri.length() == 0) return result;
|
||||
|
||||
iterator_t uri_end = uri.end();
|
||||
|
||||
// get query start
|
||||
iterator_t query_start = std::find(uri.begin(), uri_end, '?');
|
||||
|
||||
// protocol
|
||||
iterator_t protocol_start = uri.begin();
|
||||
iterator_t protocol_end = std::find(protocol_start, uri_end, ':'); //"://");
|
||||
|
||||
if (protocol_end != uri_end) {
|
||||
std::string http_protocol = &*(protocol_end);
|
||||
if ((http_protocol.length() > 3) && (http_protocol.substr(0, 3) == "://")) {
|
||||
result.protocol = std::string(protocol_start, protocol_end);
|
||||
protocol_end += 3; // ://
|
||||
} else {
|
||||
protocol_end = uri.begin(); // no protocol
|
||||
}
|
||||
} else {
|
||||
protocol_end = uri.begin(); // no protocol
|
||||
}
|
||||
|
||||
// URL
|
||||
iterator_t host_start = protocol_end;
|
||||
iterator_t path_start = std::find(host_start, uri_end, '/');
|
||||
|
||||
iterator_t host_end = std::find(protocol_end, (path_start != uri_end) ? path_start : query_start, ':');
|
||||
|
||||
result.asset_url = std::string(host_start, host_end);
|
||||
|
||||
// port
|
||||
if ((host_end != uri_end) && ((&*(host_end))[0] == ':')) { // we have a port
|
||||
host_end++;
|
||||
iterator_t portEnd = (path_start != uri_end) ? path_start : query_start;
|
||||
result.port = std::string(host_end, portEnd);
|
||||
}
|
||||
|
||||
// URI
|
||||
if (path_start != uri_end) result.asset_uri = std::string(path_start, query_start);
|
||||
|
||||
// query
|
||||
if (query_start != uri_end) result.query_string = std::string(query_start, uri.end());
|
||||
|
||||
return result;
|
||||
} // Parse
|
||||
}; // uri
|
||||
|
||||
class PracticeSection
|
||||
{
|
||||
public:
|
||||
PracticeSection(const std::string &_id, const std::string &_type, const std::string &_practice_name)
|
||||
{
|
||||
auto maybe_type = string_to_practice_type.find(_type);
|
||||
if (maybe_type == string_to_practice_type.end()) {
|
||||
dbgError(D_K8S_POLICY) << "Illegal pracrtice type: " << _type;
|
||||
return;
|
||||
}
|
||||
|
||||
type = _type;
|
||||
name = _practice_name;
|
||||
id = _id;
|
||||
}
|
||||
|
||||
void
|
||||
serialize(cereal::JSONOutputArchive &out_ar) const
|
||||
{
|
||||
out_ar(
|
||||
cereal::make_nvp("practiceId", id),
|
||||
cereal::make_nvp("practiceName", name),
|
||||
cereal::make_nvp("practiceType", type)
|
||||
);
|
||||
}
|
||||
|
||||
const std::string & getPracticeId() const { return id; }
|
||||
const std::string & getPracticeName() const { return name; }
|
||||
|
||||
private:
|
||||
std::string id;
|
||||
std::string name;
|
||||
std::string type;
|
||||
};
|
||||
|
||||
class ParametersSection
|
||||
{
|
||||
public:
|
||||
ParametersSection(
|
||||
const std::string &_id,
|
||||
const std::string &_name)
|
||||
:
|
||||
name(_name),
|
||||
id(_id)
|
||||
{
|
||||
if (_id.empty() && _name.empty()) {
|
||||
dbgError(D_K8S_POLICY) << "Illegal Parameter values. Name and ID are empty";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const std::string & getId() const { return id; }
|
||||
|
||||
void
|
||||
serialize(cereal::JSONOutputArchive &out_ar) const
|
||||
{
|
||||
out_ar(
|
||||
cereal::make_nvp("parameterId", id),
|
||||
cereal::make_nvp("parameterName", name),
|
||||
cereal::make_nvp("parameterType", type)
|
||||
);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string name;
|
||||
std::string id;
|
||||
std::string type = "Exception";
|
||||
};
|
||||
|
||||
class RulesTriggerSection
|
||||
{
|
||||
public:
|
||||
RulesTriggerSection(
|
||||
const std::string &_name,
|
||||
const std::string &_id,
|
||||
const std::string &_type)
|
||||
:
|
||||
name(_name),
|
||||
id(_id)
|
||||
{
|
||||
if (_name.empty() && _id.empty()) {
|
||||
dbgError(D_K8S_POLICY) << "Illegal values for trigger. Name and ID are empty";
|
||||
return;
|
||||
}
|
||||
auto maybe_type = string_to_trigger_type.find(_type);
|
||||
if (maybe_type == string_to_trigger_type.end()) {
|
||||
dbgError(D_K8S_POLICY) << "Illegal trigger type in rule: " << _type;
|
||||
return;
|
||||
}
|
||||
type = _type;
|
||||
}
|
||||
|
||||
const std::string & getId() const { return id; }
|
||||
const std::string & getName() const { return id; }
|
||||
|
||||
void
|
||||
serialize(cereal::JSONOutputArchive &out_ar) const
|
||||
{
|
||||
out_ar(
|
||||
cereal::make_nvp("triggerId", id),
|
||||
cereal::make_nvp("triggerName", name),
|
||||
cereal::make_nvp("triggerType", type)
|
||||
);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string name;
|
||||
std::string id;
|
||||
std::string type;
|
||||
};
|
||||
|
||||
class RulesConfigRulebase
|
||||
{
|
||||
public:
|
||||
RulesConfigRulebase()
|
||||
{}
|
||||
|
||||
RulesConfigRulebase(
|
||||
const std::string &_name,
|
||||
const std::string &_url,
|
||||
const std::string &_uri,
|
||||
std::vector<PracticeSection> _practices,
|
||||
std::vector<ParametersSection> _parameters,
|
||||
std::vector<RulesTriggerSection> _triggers)
|
||||
:
|
||||
name(_name),
|
||||
practices(_practices),
|
||||
parameters(_parameters),
|
||||
triggers(_triggers)
|
||||
{
|
||||
try {
|
||||
id = to_string(boost::uuids::random_generator()());
|
||||
bool any = _name == "Any" && _url == "Any" && _uri == "Any";
|
||||
if (_uri != "/") {
|
||||
context = any ? "All()" : "Any("
|
||||
"All("
|
||||
"Any("
|
||||
"EqualHost(" + _url + ")"
|
||||
"),"
|
||||
"EqualListeningPort(80)" +
|
||||
std::string(_uri.empty() ? "" : ",BeginWithUri(" + _uri + ")") +
|
||||
"),"
|
||||
"All("
|
||||
"Any("
|
||||
"EqualHost(" + _url + ")"
|
||||
"),"
|
||||
"EqualListeningPort(443)" +
|
||||
std::string(_uri.empty() ? "" : ",BeginWithUri(" + _uri + ")") +
|
||||
")"
|
||||
")";
|
||||
} else {
|
||||
context = any ? "All()" : "Any("
|
||||
"All("
|
||||
"Any("
|
||||
"EqualHost(" + _url + ")"
|
||||
"),"
|
||||
"EqualListeningPort(80)"
|
||||
"),"
|
||||
"All("
|
||||
"Any("
|
||||
"EqualHost(" + _url + ")"
|
||||
"),"
|
||||
"EqualListeningPort(443)"
|
||||
")"
|
||||
")";
|
||||
}
|
||||
} catch (const boost::uuids::entropy_error &e) {
|
||||
dbgWarning(D_K8S_POLICY) << "Failed to generate rule UUID. Error: " << e.what();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
serialize(cereal::JSONOutputArchive &out_ar) const
|
||||
{
|
||||
std::string empty_str = "";
|
||||
out_ar(
|
||||
cereal::make_nvp("assetId", id),
|
||||
cereal::make_nvp("assetName", name),
|
||||
cereal::make_nvp("ruleId", id),
|
||||
cereal::make_nvp("ruleName", name),
|
||||
cereal::make_nvp("context", context),
|
||||
cereal::make_nvp("priority", 1),
|
||||
cereal::make_nvp("isCleanup", false),
|
||||
cereal::make_nvp("parameters", parameters),
|
||||
cereal::make_nvp("practices", practices),
|
||||
cereal::make_nvp("triggers", triggers),
|
||||
cereal::make_nvp("zoneId", empty_str),
|
||||
cereal::make_nvp("zoneName", empty_str)
|
||||
);
|
||||
}
|
||||
|
||||
const std::string & getRuleId() const { return id; }
|
||||
const std::string & getAssetName() const { return name; }
|
||||
const std::string & getRuleName() const { return name; }
|
||||
const std::string & getAsstId() const { return id; }
|
||||
const std::string & getPracticeId() const { return practices[0].getPracticeId(); }
|
||||
const std::string & getPracticeName() const { return practices[0].getPracticeName(); }
|
||||
const std::vector<PracticeSection> & getPractice() const { return practices; }
|
||||
const std::vector<ParametersSection> & getParameters() const { return parameters; }
|
||||
const std::vector<RulesTriggerSection> & getTriggers() const { return triggers; }
|
||||
|
||||
|
||||
private:
|
||||
std::string context;
|
||||
std::string id;
|
||||
std::string name;
|
||||
std::vector<PracticeSection> practices;
|
||||
std::vector<ParametersSection> parameters;
|
||||
std::vector<RulesTriggerSection> triggers;
|
||||
};
|
||||
|
||||
class RulesConfigWrapper
|
||||
{
|
||||
public:
|
||||
class RulesConfig
|
||||
{
|
||||
public:
|
||||
RulesConfig(const std::vector<RulesConfigRulebase> &_rules_config)
|
||||
:
|
||||
rules_config(_rules_config)
|
||||
{
|
||||
sort(rules_config.begin(), rules_config.end(), sortBySpecific);
|
||||
}
|
||||
|
||||
void
|
||||
serialize(cereal::JSONOutputArchive &out_ar) const
|
||||
{
|
||||
out_ar(
|
||||
cereal::make_nvp("rulesConfig", rules_config)
|
||||
);
|
||||
}
|
||||
|
||||
private:
|
||||
static bool
|
||||
sortBySpecific(const RulesConfigRulebase &first, const RulesConfigRulebase &second)
|
||||
{
|
||||
return sortBySpecificAux(first.getAssetName(), second.getAssetName());
|
||||
}
|
||||
|
||||
static bool
|
||||
sortBySpecificAux(const std::string &first, const std::string &second)
|
||||
{
|
||||
if (first.empty()) return false;
|
||||
if (second.empty()) return true;
|
||||
|
||||
AssetUrlParser first_parsed = AssetUrlParser(first);
|
||||
AssetUrlParser second_parsed = AssetUrlParser(second);
|
||||
|
||||
// sort by URL
|
||||
if (first_parsed.asset_url == "*" && second_parsed.asset_url != "*") return false;
|
||||
if (second_parsed.asset_url == "*" && first_parsed.asset_url != "*") return true;
|
||||
|
||||
// sort by port
|
||||
if (first_parsed.port == "*" && second_parsed.port != "*") return false;
|
||||
if (second_parsed.port == "*" && first_parsed.port != "*") return true;
|
||||
|
||||
// sort by URI
|
||||
if (first_parsed.asset_uri == "*" && second_parsed.asset_uri != "*") return false;
|
||||
if (second_parsed.asset_uri == "*" && first_parsed.asset_uri != "*") return true;
|
||||
|
||||
if (first_parsed.asset_uri.empty()) return false;
|
||||
if (second_parsed.asset_uri.empty()) return true;
|
||||
|
||||
if (second_parsed.asset_uri.find(first_parsed.asset_uri) != std::string::npos) return false;
|
||||
if (first_parsed.asset_uri.find(second_parsed.asset_uri) != std::string::npos) return true;
|
||||
|
||||
if (first_parsed.asset_url.empty()) return false;
|
||||
if (second_parsed.asset_url.empty()) return false;
|
||||
|
||||
return second < first;
|
||||
}
|
||||
|
||||
std::vector<RulesConfigRulebase> rules_config;
|
||||
};
|
||||
|
||||
RulesConfigWrapper(const std::vector<RulesConfigRulebase> &_rules_config)
|
||||
:
|
||||
rules_config_rulebase(RulesConfig(_rules_config))
|
||||
{}
|
||||
|
||||
void
|
||||
serialize(cereal::JSONOutputArchive &out_ar) const
|
||||
{
|
||||
out_ar(
|
||||
cereal::make_nvp("rulebase", rules_config_rulebase)
|
||||
);
|
||||
}
|
||||
|
||||
private:
|
||||
RulesConfig rules_config_rulebase;
|
||||
};
|
||||
|
||||
#endif // __RULES_CONFIG_SECTION_H__
|
||||
@@ -0,0 +1,121 @@
|
||||
// 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 __SETTINGS_SECTION_H__
|
||||
#define __SETTINGS_SECTION_H__
|
||||
|
||||
#include <cereal/archives/json.hpp>
|
||||
#include <boost/uuid/uuid.hpp>
|
||||
#include <boost/uuid/uuid_generators.hpp>
|
||||
#include <boost/uuid/uuid_io.hpp>
|
||||
|
||||
#include "config.h"
|
||||
#include "debug.h"
|
||||
#include "k8s_policy_common.h"
|
||||
|
||||
USE_DEBUG_FLAG(D_K8S_POLICY);
|
||||
|
||||
class AgentSettingsSection
|
||||
{
|
||||
public:
|
||||
AgentSettingsSection(
|
||||
const std::string &_key,
|
||||
const std::string &_value)
|
||||
:
|
||||
key(_key),
|
||||
value(_value)
|
||||
{
|
||||
try {
|
||||
id = to_string(boost::uuids::random_generator()());
|
||||
} catch (const boost::uuids::entropy_error &e) {
|
||||
dbgWarning(D_K8S_POLICY) << "Failed to generate agent setting UUID. Error: " << e.what();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
serialize(cereal::JSONOutputArchive &out_ar) const
|
||||
{
|
||||
out_ar(
|
||||
cereal::make_nvp("id", id),
|
||||
cereal::make_nvp("key", key),
|
||||
cereal::make_nvp("value", value)
|
||||
);
|
||||
}
|
||||
|
||||
const std::string & getSettingId() const { return id; }
|
||||
|
||||
private:
|
||||
std::string id;
|
||||
std::string key;
|
||||
std::string value;
|
||||
};
|
||||
|
||||
class SettingsRulebase
|
||||
{
|
||||
public:
|
||||
SettingsRulebase(std::vector<AgentSettingsSection> _agentSettings) : agentSettings(_agentSettings) {}
|
||||
|
||||
void
|
||||
serialize(cereal::JSONOutputArchive &out_ar) const
|
||||
{
|
||||
std::string profile_type = "Kubernetes";
|
||||
std::string upgrade_mode = "automatic";
|
||||
out_ar(
|
||||
cereal::make_nvp("agentSettings", agentSettings),
|
||||
cereal::make_nvp("agentType", profile_type),
|
||||
cereal::make_nvp("allowOnlyDefinedApplications", false),
|
||||
cereal::make_nvp("anyFog", true),
|
||||
cereal::make_nvp("maxNumberOfAgents", 10),
|
||||
cereal::make_nvp("upgradeMode", upgrade_mode)
|
||||
);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<AgentSettingsSection> agentSettings;
|
||||
};
|
||||
|
||||
class SettingsWrapper
|
||||
{
|
||||
public:
|
||||
SettingsWrapper(SettingsRulebase _agent) : agent(_agent)
|
||||
{
|
||||
try {
|
||||
id = to_string(boost::uuids::random_generator()());
|
||||
} catch (const boost::uuids::entropy_error &e) {
|
||||
dbgWarning(D_K8S_POLICY) << "Failed to generate Settings Wrapper UUID. Error: " << e.what();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
serialize(cereal::JSONOutputArchive &out_ar) const
|
||||
{
|
||||
out_ar(
|
||||
cereal::make_nvp("profileType", profileType),
|
||||
cereal::make_nvp("tokenType", isToken),
|
||||
cereal::make_nvp("tokenType", tokenType),
|
||||
cereal::make_nvp("name", name),
|
||||
cereal::make_nvp("id", id),
|
||||
cereal::make_nvp("agent", agent)
|
||||
);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string profileType = "agent";
|
||||
bool isToken = true;
|
||||
std::string tokenType = "sameToken";
|
||||
std::string id;
|
||||
std::string name = "Kubernetes Agents";
|
||||
SettingsRulebase agent;
|
||||
};
|
||||
|
||||
#endif // __SETTINGS_SECTION_H__
|
||||
@@ -0,0 +1,79 @@
|
||||
// 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 __SNORT_SECTION_H__
|
||||
#define __SNORT_SECTION_H__
|
||||
|
||||
#include <cereal/archives/json.hpp>
|
||||
#include <boost/uuid/uuid.hpp>
|
||||
#include <boost/uuid/uuid_generators.hpp>
|
||||
#include <boost/uuid/uuid_io.hpp>
|
||||
|
||||
#include "config.h"
|
||||
#include "debug.h"
|
||||
|
||||
USE_DEBUG_FLAG(D_K8S_POLICY);
|
||||
|
||||
class AgentSettingsSection
|
||||
{
|
||||
public:
|
||||
AgentSettingsSection(std::string _key, std::string _value) : key(_key), value(_value)
|
||||
{
|
||||
try {
|
||||
id = to_string(boost::uuids::random_generator()());
|
||||
} catch (const boost::uuids::entropy_error &e) {
|
||||
dbgWarning(D_K8S_POLICY) << "Failed to generate agent setting UUID. Error: " << e.what();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
serialize(cereal::JSONOutputArchive &out_ar) const
|
||||
{
|
||||
out_ar(
|
||||
cereal::make_nvp("id", id),
|
||||
cereal::make_nvp("key", key),
|
||||
cereal::make_nvp("value", value)
|
||||
);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string id;
|
||||
std::string key;
|
||||
std::string value;
|
||||
};
|
||||
|
||||
class IpsSnortSigsRulebase
|
||||
{
|
||||
public:
|
||||
IpsSnortSigsRulebase(std::vector<AgentSettingsSection> _agentSettings) : agentSettings(_agentSettings) {}
|
||||
|
||||
void
|
||||
serialize(cereal::JSONOutputArchive &out_ar) const
|
||||
{
|
||||
std::string profile_type = "KubernetesProfile";
|
||||
std::string upgrade_mode = "automatic";
|
||||
out_ar(
|
||||
cereal::make_nvp("agentSettings", agentSettings),
|
||||
cereal::make_nvp("agentType", profile_type),
|
||||
cereal::make_nvp("allowOnlyDefinedApplications", false),
|
||||
cereal::make_nvp("anyFog", true),
|
||||
cereal::make_nvp("maxNumberOfAgents", 10),
|
||||
cereal::make_nvp("upgradeMode", upgrade_mode)
|
||||
);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<AgentSettingsSection> agentSettings;
|
||||
};
|
||||
|
||||
#endif // __SNORT_SECTION_H__
|
||||
@@ -0,0 +1,625 @@
|
||||
// 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 __TRIGGERS_SECTION_H__
|
||||
#define __TRIGGERS_SECTION_H__
|
||||
|
||||
#include <cereal/archives/json.hpp>
|
||||
#include <boost/uuid/uuid.hpp>
|
||||
#include <boost/uuid/uuid_generators.hpp>
|
||||
#include <boost/uuid/uuid_io.hpp>
|
||||
|
||||
#include "config.h"
|
||||
#include "debug.h"
|
||||
#include "k8s_policy_common.h"
|
||||
|
||||
USE_DEBUG_FLAG(D_K8S_POLICY);
|
||||
|
||||
class LogTriggerSection
|
||||
{
|
||||
public:
|
||||
LogTriggerSection()
|
||||
{}
|
||||
|
||||
LogTriggerSection(
|
||||
const std::string &_name,
|
||||
const std::string &_verbosity,
|
||||
const std::string &_extendloggingMinSeverity,
|
||||
bool _extendlogging,
|
||||
bool _logToAgent,
|
||||
bool _logToCef,
|
||||
bool _logToCloud,
|
||||
bool _logToSyslog,
|
||||
bool _responseBody,
|
||||
bool _tpDetect,
|
||||
bool _tpPrevent,
|
||||
bool _webBody,
|
||||
bool _webHeaders,
|
||||
bool _webRequests,
|
||||
bool _webUrlPath,
|
||||
bool _webUrlQuery,
|
||||
int _cefPortNum,
|
||||
const std::string &_cefIpAddress,
|
||||
int _syslogPortNum,
|
||||
const std::string &_syslogIpAddress,
|
||||
bool _beautify_logs)
|
||||
:
|
||||
name(_name),
|
||||
verbosity(_verbosity),
|
||||
extendloggingMinSeverity(_extendloggingMinSeverity),
|
||||
extendlogging(_extendlogging),
|
||||
logToAgent(_logToAgent),
|
||||
logToCef(_logToCef),
|
||||
logToCloud(_logToCloud),
|
||||
logToSyslog(_logToSyslog),
|
||||
responseBody(_responseBody),
|
||||
tpDetect(_tpDetect),
|
||||
tpPrevent(_tpPrevent),
|
||||
webBody(_webBody),
|
||||
webHeaders(_webHeaders),
|
||||
webRequests(_webRequests),
|
||||
webUrlPath(_webUrlPath),
|
||||
webUrlQuery(_webUrlQuery),
|
||||
cefPortNum (_cefPortNum),
|
||||
cefIpAddress (_cefIpAddress),
|
||||
syslogPortNum (_syslogPortNum),
|
||||
syslogIpAddress (_syslogIpAddress),
|
||||
beautify_logs(_beautify_logs)
|
||||
{
|
||||
try {
|
||||
id = to_string(boost::uuids::random_generator()());
|
||||
context = "triggerId(" + id + ")";
|
||||
} catch (const boost::uuids::entropy_error &e) {
|
||||
dbgWarning(D_K8S_POLICY) << "Failed to generate log trigger UUID. Error: " << e.what();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
serialize(cereal::JSONOutputArchive &out_ar) const
|
||||
{
|
||||
std::string trigger_type = "log";
|
||||
std::string urlForSyslog = syslogIpAddress + ":" + std::to_string(syslogPortNum);
|
||||
std::string urlForCef = cefIpAddress + ":" + std::to_string(cefPortNum);
|
||||
out_ar(
|
||||
cereal::make_nvp("context", context),
|
||||
cereal::make_nvp("triggerName", name),
|
||||
cereal::make_nvp("triggerType", trigger_type),
|
||||
cereal::make_nvp("verbosity", verbosity),
|
||||
cereal::make_nvp("acAllow", false),
|
||||
cereal::make_nvp("acDrop", false),
|
||||
cereal::make_nvp("complianceViolations", false),
|
||||
cereal::make_nvp("complianceWarnings", false),
|
||||
cereal::make_nvp("extendloggingMinSeverity", extendloggingMinSeverity),
|
||||
cereal::make_nvp("extendlogging", extendlogging),
|
||||
cereal::make_nvp("logToAgent", logToAgent),
|
||||
cereal::make_nvp("logToCef", logToCef),
|
||||
cereal::make_nvp("logToCloud", logToCloud),
|
||||
cereal::make_nvp("logToSyslog", logToSyslog),
|
||||
cereal::make_nvp("responseBody", responseBody),
|
||||
cereal::make_nvp("responseCode", false),
|
||||
cereal::make_nvp("tpDetect", tpDetect),
|
||||
cereal::make_nvp("tpPrevent", tpPrevent),
|
||||
cereal::make_nvp("webBody", webBody),
|
||||
cereal::make_nvp("webHeaders", webHeaders),
|
||||
cereal::make_nvp("webRequests", webRequests),
|
||||
cereal::make_nvp("webUrlPath", webUrlPath),
|
||||
cereal::make_nvp("webUrlQuery", webUrlQuery),
|
||||
cereal::make_nvp("urlForSyslog", urlForSyslog),
|
||||
cereal::make_nvp("urlForCef", urlForCef),
|
||||
cereal::make_nvp("formatLoggingOutput", beautify_logs)
|
||||
);
|
||||
}
|
||||
|
||||
const std::string & getTriggerId() const { return id; }
|
||||
const std::string & getTriggerName() const { return name; }
|
||||
|
||||
private:
|
||||
std::string id;
|
||||
std::string name;
|
||||
std::string context;
|
||||
std::string verbosity;
|
||||
std::string extendloggingMinSeverity;
|
||||
bool extendlogging;
|
||||
bool logToAgent;
|
||||
bool logToCef;
|
||||
bool logToCloud;
|
||||
bool logToSyslog;
|
||||
bool responseBody;
|
||||
bool tpDetect;
|
||||
bool tpPrevent;
|
||||
bool webBody;
|
||||
bool webHeaders;
|
||||
bool webRequests;
|
||||
bool webUrlPath;
|
||||
bool webUrlQuery;
|
||||
int cefPortNum;
|
||||
std::string cefIpAddress;
|
||||
int syslogPortNum;
|
||||
std::string syslogIpAddress;
|
||||
bool beautify_logs;
|
||||
};
|
||||
|
||||
class WebUserResponseTriggerSection
|
||||
{
|
||||
public:
|
||||
WebUserResponseTriggerSection(
|
||||
const std::string &_name,
|
||||
const std::string &_details_level,
|
||||
const std::string &_response_body,
|
||||
int _response_code,
|
||||
const std::string &_response_title)
|
||||
:
|
||||
name(_name),
|
||||
context(),
|
||||
details_level(_details_level),
|
||||
response_body(_response_body),
|
||||
response_title(_response_title),
|
||||
response_code(_response_code)
|
||||
{
|
||||
try {
|
||||
id = to_string(boost::uuids::random_generator()());
|
||||
context = "triggerId(" + id + ")";
|
||||
} catch (const boost::uuids::entropy_error &e) {
|
||||
dbgWarning(D_K8S_POLICY) << "Failed to generate webUserResponse trigger UUID. Error: " << e.what();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
serialize(cereal::JSONOutputArchive &out_ar) const
|
||||
{
|
||||
out_ar(
|
||||
cereal::make_nvp("context", context),
|
||||
cereal::make_nvp("triggerName", name),
|
||||
cereal::make_nvp("details level", details_level),
|
||||
cereal::make_nvp("response body", response_body),
|
||||
cereal::make_nvp("response code", response_code),
|
||||
cereal::make_nvp("response title", response_title)
|
||||
);
|
||||
}
|
||||
|
||||
const std::string & getTriggerId() const { return id; }
|
||||
const std::string & getTriggerName() const { return name; }
|
||||
|
||||
private:
|
||||
std::string id;
|
||||
std::string name;
|
||||
std::string context;
|
||||
std::string details_level;
|
||||
std::string response_body;
|
||||
std::string response_title;
|
||||
int response_code;
|
||||
};
|
||||
|
||||
class AppSecWebUserResponseSpec
|
||||
{
|
||||
public:
|
||||
void
|
||||
load(cereal::JSONInputArchive &archive_in)
|
||||
{
|
||||
dbgTrace(D_K8S_POLICY) << "Loading AppSec web user response spec";
|
||||
parseAppsecJSONKey<int>("http-response-code", httpResponseCode, archive_in, 403);
|
||||
parseAppsecJSONKey<std::string>("mode", mode, archive_in, "block-page");
|
||||
if (mode == "block-page") {
|
||||
parseAppsecJSONKey<std::string>(
|
||||
"message-body",
|
||||
messageBody,
|
||||
archive_in,
|
||||
"Openappsec's <b>Application Security</b> has detected an attack and blocked it."
|
||||
);
|
||||
parseAppsecJSONKey<std::string>(
|
||||
"message-title",
|
||||
messageTitle,
|
||||
archive_in,
|
||||
"Attack blocked by web application protection"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
int getHttpResponseCode() const { return httpResponseCode; }
|
||||
const std::string & getMessageBody() const { return messageBody; }
|
||||
const std::string & getMessageTitle() const { return messageTitle; }
|
||||
const std::string & getMode() const { return mode; }
|
||||
|
||||
private:
|
||||
int httpResponseCode;
|
||||
std::string messageBody;
|
||||
std::string messageTitle;
|
||||
std::string mode;
|
||||
};
|
||||
|
||||
std::ostream &
|
||||
operator<<(std::ostream &os, const AppSecWebUserResponseSpec &obj)
|
||||
{
|
||||
os
|
||||
<< "mode: "
|
||||
<< obj.getMode()
|
||||
<< "," << std::endl << "message-title: "
|
||||
<< obj.getMessageTitle()
|
||||
<< "," << std::endl << "message-body: "
|
||||
<< obj.getMessageBody()
|
||||
<< "," << std::endl << "http-response-code: "
|
||||
<< obj.getHttpResponseCode();
|
||||
return os;
|
||||
}
|
||||
|
||||
class TriggersRulebase
|
||||
{
|
||||
public:
|
||||
TriggersRulebase(
|
||||
std::vector<LogTriggerSection> _logTriggers,
|
||||
std::vector<WebUserResponseTriggerSection> _webUserResponseTriggers)
|
||||
:
|
||||
logTriggers(_logTriggers),
|
||||
webUserResponseTriggers(_webUserResponseTriggers) {}
|
||||
|
||||
|
||||
void
|
||||
serialize(cereal::JSONOutputArchive &out_ar) const
|
||||
{
|
||||
out_ar(
|
||||
cereal::make_nvp("log", logTriggers),
|
||||
cereal::make_nvp("webUserResponse", webUserResponseTriggers)
|
||||
);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<LogTriggerSection> logTriggers;
|
||||
std::vector<WebUserResponseTriggerSection> webUserResponseTriggers;
|
||||
};
|
||||
|
||||
class AppsecTriggerAccessControlLogging
|
||||
{
|
||||
public:
|
||||
void
|
||||
load(cereal::JSONInputArchive &archive_in)
|
||||
{
|
||||
dbgTrace(D_K8S_POLICY) << "Loading AppSec Trigger - Access Control Logging";
|
||||
parseAppsecJSONKey<bool>("allow-events", allow_events, archive_in, false);
|
||||
parseAppsecJSONKey<bool>("drop-events", drop_events, archive_in, false);
|
||||
}
|
||||
|
||||
bool isAllowEvents() const { return allow_events; }
|
||||
bool isDropEvents() const { return drop_events; }
|
||||
|
||||
private:
|
||||
bool allow_events = false;
|
||||
bool drop_events = false;
|
||||
};
|
||||
|
||||
std::ostream &
|
||||
operator<<(std::ostream &os, const AppsecTriggerAccessControlLogging &obj)
|
||||
{
|
||||
os
|
||||
<< "AppSec Trigger - Access Control Logging: "
|
||||
<< "isAllowEvents: "
|
||||
<< obj.isAllowEvents()
|
||||
<< " , isDropEvents: "
|
||||
<< obj.isDropEvents();
|
||||
return os;
|
||||
}
|
||||
|
||||
class AppsecTriggerAdditionalSuspiciousEventsLogging : public ClientRest
|
||||
{
|
||||
public:
|
||||
void
|
||||
load(cereal::JSONInputArchive &archive_in)
|
||||
{
|
||||
dbgTrace(D_K8S_POLICY) << "Loading AppSec Trigger - Additional Suspicious Events Logging";
|
||||
parseAppsecJSONKey<bool>("enabled", enabled, archive_in, true);
|
||||
parseAppsecJSONKey<bool>("response-body", response_body, archive_in, false);
|
||||
parseAppsecJSONKey<std::string>("minimum-severity", minimum_severity, archive_in, "high");
|
||||
}
|
||||
|
||||
bool isEnabled() const { return enabled; }
|
||||
bool isResponseBody() const { return response_body; }
|
||||
const std::string & getMinimumSeverity() const { return minimum_severity; }
|
||||
|
||||
private:
|
||||
bool enabled = true;
|
||||
bool response_body = false;
|
||||
std::string minimum_severity = "high";
|
||||
};
|
||||
|
||||
std::ostream &
|
||||
operator<<(std::ostream &os, const AppsecTriggerAdditionalSuspiciousEventsLogging &obj)
|
||||
{
|
||||
os
|
||||
<< "AppsecTriggerAdditionalSuspiciousEventsLogging: "
|
||||
<< "Enabled: "
|
||||
<< obj.isEnabled()
|
||||
<< " response_body: "
|
||||
<< obj.isResponseBody()
|
||||
<< " minimum_severity: "
|
||||
<< obj.getMinimumSeverity();
|
||||
return os;
|
||||
}
|
||||
|
||||
class AppsecTriggerLogging : public ClientRest
|
||||
{
|
||||
public:
|
||||
void
|
||||
load(cereal::JSONInputArchive &archive_in)
|
||||
{
|
||||
dbgTrace(D_K8S_POLICY) << "Loading AppSec Trigger Logging";
|
||||
parseAppsecJSONKey<bool>("all-web-requests", all_web_requests, archive_in, false);
|
||||
parseAppsecJSONKey<bool>("detect-events", detect_events, archive_in, false);
|
||||
parseAppsecJSONKey<bool>("prevent-events", prevent_events, archive_in, true);
|
||||
}
|
||||
|
||||
bool isAllWebRequests() const { return all_web_requests; }
|
||||
|
||||
bool isDetectEvents() const { return detect_events; }
|
||||
|
||||
bool isPreventEvents() const { return prevent_events; }
|
||||
|
||||
private:
|
||||
bool all_web_requests = false;
|
||||
bool detect_events = false;
|
||||
bool prevent_events = true;
|
||||
};
|
||||
|
||||
std::ostream &
|
||||
operator<<(std::ostream &os, const AppsecTriggerLogging &obj)
|
||||
{
|
||||
os
|
||||
<< "AppsecTriggerLogging: "
|
||||
<< "all_web_requests: "
|
||||
<< obj.isAllWebRequests()
|
||||
<< ", detect_events: "
|
||||
<< obj.isDetectEvents()
|
||||
<< ", prevent_events: "
|
||||
<< obj.isPreventEvents();
|
||||
return os;
|
||||
}
|
||||
|
||||
class AppsecTriggerExtendedLogging : public ClientRest
|
||||
{
|
||||
public:
|
||||
void
|
||||
load(cereal::JSONInputArchive &archive_in)
|
||||
{
|
||||
dbgTrace(D_K8S_POLICY) << "Loading AppSec Trigger Extended Logging";
|
||||
parseAppsecJSONKey<bool>("http-headers", http_headers, archive_in, false);
|
||||
parseAppsecJSONKey<bool>("request-body", request_body, archive_in, false);
|
||||
parseAppsecJSONKey<bool>("url-path", url_path, archive_in, false);
|
||||
parseAppsecJSONKey<bool>("url-query", url_query, archive_in, false);
|
||||
}
|
||||
|
||||
bool isHttpHeaders() const { return http_headers; }
|
||||
bool isRequestBody() const { return request_body; }
|
||||
bool isUrlPath() const { return url_path; }
|
||||
bool isUrlQuery() const { return url_query; }
|
||||
|
||||
private:
|
||||
bool http_headers = false;
|
||||
bool request_body = false;
|
||||
bool url_path = false;
|
||||
bool url_query = false;
|
||||
};
|
||||
|
||||
std::ostream &
|
||||
operator<<(std::ostream &os, const AppsecTriggerExtendedLogging &obj)
|
||||
{
|
||||
os
|
||||
<< "AppsecTriggerExtendedLogging: "
|
||||
<< "http_headers: "
|
||||
<< obj.isHttpHeaders()
|
||||
<< ", request_body: "
|
||||
<< obj.isRequestBody()
|
||||
<< ", url_path: "
|
||||
<< obj.isUrlPath()
|
||||
<< ", url_query: "
|
||||
<< obj.isUrlQuery();
|
||||
return os;
|
||||
}
|
||||
|
||||
class LoggingService
|
||||
{
|
||||
public:
|
||||
void
|
||||
load(cereal::JSONInputArchive &archive_in)
|
||||
{
|
||||
parseAppsecJSONKey<std::string>("address", address, archive_in);
|
||||
parseAppsecJSONKey<std::string>("proto", proto, archive_in);
|
||||
parseAppsecJSONKey<int>("port", port, archive_in, 514);
|
||||
}
|
||||
|
||||
const std::string & getAddress() const { return address; }
|
||||
const std::string & getProto() const { return proto; }
|
||||
int getPort() const { return port; }
|
||||
|
||||
private:
|
||||
std::string address;
|
||||
std::string proto;
|
||||
int port = 514;
|
||||
};
|
||||
|
||||
class StdoutLogging
|
||||
{
|
||||
public:
|
||||
StdoutLogging() : format("json") {}
|
||||
|
||||
void
|
||||
load(cereal::JSONInputArchive &archive_in)
|
||||
{
|
||||
parseAppsecJSONKey<std::string>("format", format, archive_in, "json");
|
||||
}
|
||||
|
||||
const std::string & getFormat() const { return format; }
|
||||
|
||||
private:
|
||||
std::string format;
|
||||
};
|
||||
|
||||
class AppsecTriggerLogDestination : public ClientRest
|
||||
{
|
||||
public:
|
||||
void
|
||||
load(cereal::JSONInputArchive &archive_in)
|
||||
{
|
||||
dbgError(D_K8S_POLICY) << "AppsecTriggerLogDestination load";
|
||||
// TBD: support "file"
|
||||
parseAppsecJSONKey<bool>("cloud", cloud, archive_in, false);
|
||||
|
||||
StdoutLogging stdout_log;
|
||||
parseAppsecJSONKey<StdoutLogging>("stdout", stdout_log, archive_in);
|
||||
agent_local = !(stdout_log.getFormat().empty());
|
||||
beautify_logs = stdout_log.getFormat() == "json-formatted";
|
||||
parseAppsecJSONKey<LoggingService>("syslog-service", syslog_service, archive_in);
|
||||
parseAppsecJSONKey<LoggingService>("cef-service", cef_service, archive_in);
|
||||
}
|
||||
|
||||
int getCefServerUdpPort() const { return getCefServiceData().getPort(); }
|
||||
int getSyslogServerUdpPort() const { return getSyslogServiceData().getPort(); }
|
||||
bool isAgentLocal() const { return agent_local; }
|
||||
bool shouldBeautifyLogs() const { return beautify_logs; }
|
||||
|
||||
bool getCloud() const { return cloud; }
|
||||
bool isCefNeeded() const { return !getCefServiceData().getAddress().empty(); }
|
||||
bool isSyslogNeeded() const { return !getSyslogServiceData().getAddress().empty(); }
|
||||
const std::string & getSyslogServerIpv4Address() const { return getSyslogServiceData().getAddress(); }
|
||||
const std::string & getCefServerIpv4Address() const { return getCefServiceData().getAddress(); }
|
||||
|
||||
private:
|
||||
const LoggingService & getSyslogServiceData() const { return syslog_service; }
|
||||
const LoggingService & getCefServiceData() const { return cef_service; }
|
||||
|
||||
bool cloud = false;
|
||||
bool agent_local = true;
|
||||
bool beautify_logs = true;
|
||||
LoggingService syslog_service;
|
||||
LoggingService cef_service;
|
||||
};
|
||||
|
||||
std::ostream &
|
||||
operator<<(std::ostream &os, const AppsecTriggerLogDestination &obj)
|
||||
{
|
||||
os
|
||||
<< "AppSec Trigger Log Destination:" << std::endl
|
||||
<< "agent_local: "
|
||||
<< obj.isAgentLocal()
|
||||
<< ", beautify_logs: "
|
||||
<< obj.shouldBeautifyLogs()
|
||||
<< ", cef_server_udp_port: "
|
||||
<< obj.getCefServerUdpPort()
|
||||
<< ", syslog_server_udp_port: "
|
||||
<< obj.getSyslogServerUdpPort()
|
||||
<< ", cef_service: "
|
||||
<< obj.isCefNeeded()
|
||||
<< ", cloud: "
|
||||
<< obj.getCloud()
|
||||
<< ", syslog: "
|
||||
<< obj.isSyslogNeeded()
|
||||
<< ", syslog_server_ipv4_address: "
|
||||
<< obj.getSyslogServerIpv4Address()
|
||||
<< ", cef_server_ipv4_address: "
|
||||
<< obj.getCefServerIpv4Address();
|
||||
return os;
|
||||
}
|
||||
|
||||
class AppsecTriggerSpec
|
||||
{
|
||||
public:
|
||||
void
|
||||
load(cereal::JSONInputArchive &archive_in)
|
||||
{
|
||||
dbgTrace(D_K8S_POLICY) << "Loading AppSec trigger spec";
|
||||
parseAppsecJSONKey<AppsecTriggerAccessControlLogging>(
|
||||
"access-control-logging",
|
||||
access_control_logging,
|
||||
archive_in
|
||||
);
|
||||
parseAppsecJSONKey<AppsecTriggerAdditionalSuspiciousEventsLogging>(
|
||||
"additional-suspicious-events-logging",
|
||||
additional_suspicious_events_logging,
|
||||
archive_in
|
||||
);
|
||||
parseAppsecJSONKey<AppsecTriggerLogging>("appsec-logging", appsec_logging, archive_in);
|
||||
parseAppsecJSONKey<AppsecTriggerExtendedLogging>("extended-logging", extended_logging, archive_in);
|
||||
parseAppsecJSONKey<AppsecTriggerLogDestination>("log-destination", log_destination, archive_in);
|
||||
}
|
||||
|
||||
const AppsecTriggerAccessControlLogging &
|
||||
getAppsecTriggerAccessControlLogging() const
|
||||
{
|
||||
return access_control_logging;
|
||||
}
|
||||
|
||||
const AppsecTriggerAdditionalSuspiciousEventsLogging &
|
||||
getAppsecTriggerAdditionalSuspiciousEventsLogging() const
|
||||
{
|
||||
return additional_suspicious_events_logging;
|
||||
}
|
||||
|
||||
const AppsecTriggerLogging &
|
||||
getAppsecTriggerLogging() const
|
||||
{
|
||||
return appsec_logging;
|
||||
}
|
||||
|
||||
const AppsecTriggerExtendedLogging &
|
||||
getAppsecTriggerExtendedLogging() const
|
||||
{
|
||||
return extended_logging;
|
||||
}
|
||||
|
||||
const AppsecTriggerLogDestination &
|
||||
getAppsecTriggerLogDestination() const
|
||||
{
|
||||
return log_destination;
|
||||
}
|
||||
|
||||
private:
|
||||
AppsecTriggerAccessControlLogging access_control_logging;
|
||||
AppsecTriggerAdditionalSuspiciousEventsLogging additional_suspicious_events_logging;
|
||||
AppsecTriggerLogging appsec_logging;
|
||||
AppsecTriggerExtendedLogging extended_logging;
|
||||
AppsecTriggerLogDestination log_destination;
|
||||
};
|
||||
|
||||
std::ostream &
|
||||
operator<<(std::ostream &os, const AppsecTriggerSpec &obj)
|
||||
{
|
||||
os
|
||||
<< "AppSec Access Control Logging:" << std::endl
|
||||
<< obj.getAppsecTriggerAccessControlLogging()
|
||||
<< std::endl << "AppSec Additional Suspocious Events Logging:" << std::endl
|
||||
<< obj.getAppsecTriggerAdditionalSuspiciousEventsLogging()
|
||||
<< std::endl << "AppSec Trigger Logging:" << std::endl
|
||||
<< obj.getAppsecTriggerLogging()
|
||||
<< std::endl << "Appsec Trigger Extended Logging:" << std::endl
|
||||
<< obj.getAppsecTriggerExtendedLogging()
|
||||
<< std::endl << "AppSec Trigger Log Destination:" << std::endl
|
||||
<< obj.getAppsecTriggerLogDestination();
|
||||
return os;
|
||||
}
|
||||
|
||||
class TriggersWrapper
|
||||
{
|
||||
public:
|
||||
TriggersWrapper(const TriggersRulebase &_triggers) : triggers_rulebase(_triggers)
|
||||
{}
|
||||
|
||||
void
|
||||
serialize(cereal::JSONOutputArchive &out_ar) const
|
||||
{
|
||||
out_ar(
|
||||
cereal::make_nvp("rulebase", triggers_rulebase)
|
||||
);
|
||||
}
|
||||
|
||||
private:
|
||||
TriggersRulebase triggers_rulebase;
|
||||
};
|
||||
|
||||
#endif // __TRIGGERS_SECTION_H__
|
||||
@@ -0,0 +1,186 @@
|
||||
// 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 __TRUSTED_SOURCES_SECTION_H__
|
||||
#define __TRUSTED_SOURCES_SECTION_H__
|
||||
|
||||
#include <string>
|
||||
#include <cereal/archives/json.hpp>
|
||||
#include <boost/uuid/uuid.hpp>
|
||||
#include <boost/uuid/uuid_generators.hpp>
|
||||
#include <boost/uuid/uuid_io.hpp>
|
||||
|
||||
#include "config.h"
|
||||
#include "debug.h"
|
||||
#include "k8s_policy_common.h"
|
||||
|
||||
USE_DEBUG_FLAG(D_K8S_POLICY);
|
||||
|
||||
class TrustedSourcesSpec
|
||||
{
|
||||
public:
|
||||
void
|
||||
load(cereal::JSONInputArchive &archive_in)
|
||||
{
|
||||
dbgTrace(D_K8S_POLICY) << "Loading trusted sources spec";
|
||||
parseAppsecJSONKey<int>("minNumOfSources", min_num_of_sources, archive_in, 3);
|
||||
parseAppsecJSONKey<std::vector<std::string>>("sourcesIdentifiers", sources_identifiers, archive_in);
|
||||
}
|
||||
|
||||
int
|
||||
getMinNumOfSources() const
|
||||
{
|
||||
return min_num_of_sources;
|
||||
}
|
||||
|
||||
const std::vector<std::string> &
|
||||
getSourcesIdentifiers() const
|
||||
{
|
||||
return sources_identifiers;
|
||||
}
|
||||
|
||||
private:
|
||||
int min_num_of_sources;
|
||||
std::vector<std::string> sources_identifiers;
|
||||
};
|
||||
|
||||
std::ostream &
|
||||
operator<<(std::ostream &os, const TrustedSourcesSpec &obj)
|
||||
{
|
||||
os
|
||||
<< "Min number of sources: "
|
||||
<< obj.getMinNumOfSources()
|
||||
<< ", SourceIdentifiers: ["
|
||||
<< makeSeparatedStr(obj.getSourcesIdentifiers(), ",")
|
||||
<< "]";
|
||||
return os;
|
||||
}
|
||||
|
||||
class SourcesIdentifiers
|
||||
{
|
||||
public:
|
||||
SourcesIdentifiers(const std::string &_source_identifier, const std::string &_value)
|
||||
:
|
||||
source_identifier(_source_identifier),
|
||||
value(_value)
|
||||
{}
|
||||
|
||||
void
|
||||
save(cereal::JSONOutputArchive &out_ar) const
|
||||
{
|
||||
out_ar(
|
||||
cereal::make_nvp("sourceIdentifier", source_identifier),
|
||||
cereal::make_nvp("value", value)
|
||||
);
|
||||
}
|
||||
|
||||
const std::string &
|
||||
getSourceIdent() const
|
||||
{
|
||||
return source_identifier;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string source_identifier;
|
||||
std::string value;
|
||||
};
|
||||
|
||||
class SourceIdentifierSpec
|
||||
{
|
||||
public:
|
||||
void
|
||||
load(cereal::JSONInputArchive &archive_in)
|
||||
{
|
||||
dbgTrace(D_K8S_POLICY) << "Loading trusted sources spec";
|
||||
parseAppsecJSONKey<std::string>("sourceIdentifier", source_identifier, archive_in);
|
||||
parseAppsecJSONKey<std::vector<std::string>>("value", value, archive_in);
|
||||
}
|
||||
|
||||
const std::string &
|
||||
getSourceIdentifier() const
|
||||
{
|
||||
return source_identifier;
|
||||
}
|
||||
|
||||
const std::vector<std::string> &
|
||||
getValues() const
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string source_identifier;
|
||||
std::vector<std::string> value;
|
||||
};
|
||||
|
||||
std::ostream &
|
||||
operator<<(std::ostream &os, const SourceIdentifierSpec &obj)
|
||||
{
|
||||
os
|
||||
<< "sourceIdentifier: "
|
||||
<< obj.getSourceIdentifier()
|
||||
<< ", values: ["
|
||||
<< makeSeparatedStr(obj.getValues(), ",")
|
||||
<< "]";
|
||||
return os;
|
||||
}
|
||||
|
||||
class AppSecTrustedSources
|
||||
{
|
||||
public:
|
||||
AppSecTrustedSources()
|
||||
{}
|
||||
|
||||
AppSecTrustedSources(
|
||||
const std::string &_name,
|
||||
int _num_of_sources,
|
||||
const std::vector<SourcesIdentifiers> &_sources_identifiers)
|
||||
:
|
||||
name(_name),
|
||||
num_of_sources(_num_of_sources),
|
||||
sources_identifiers(_sources_identifiers)
|
||||
{
|
||||
try {
|
||||
id = to_string(boost::uuids::random_generator()());
|
||||
} catch (const boost::uuids::entropy_error &e) {
|
||||
dbgWarning(D_K8S_POLICY) << "Failed to generate Trusted Sources ID. Error: " << e.what();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
save(cereal::JSONOutputArchive &out_ar) const
|
||||
{
|
||||
std::string parameter_type = "TrustedSource";
|
||||
out_ar(
|
||||
cereal::make_nvp("id", id),
|
||||
cereal::make_nvp("name", name),
|
||||
cereal::make_nvp("numOfSources", num_of_sources),
|
||||
cereal::make_nvp("sourcesIdentifiers", sources_identifiers),
|
||||
cereal::make_nvp("parameterType", parameter_type)
|
||||
);
|
||||
}
|
||||
|
||||
const std::vector<SourcesIdentifiers> &
|
||||
getSourcesIdentifiers() const
|
||||
{
|
||||
return sources_identifiers;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string id;
|
||||
std::string name;
|
||||
int num_of_sources;
|
||||
std::vector<SourcesIdentifiers> sources_identifiers;
|
||||
};
|
||||
|
||||
#endif // __TRUSTED_SOURCES_SECTION_H__
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,3 @@
|
||||
add_library(manifest_controller manifest_controller.cc manifest_diff_calculator.cc manifest_handler.cc)
|
||||
|
||||
add_subdirectory(manifest_controller_ut)
|
||||
@@ -0,0 +1,445 @@
|
||||
// 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 "manifest_controller.h"
|
||||
|
||||
#include "config.h"
|
||||
#include "debug.h"
|
||||
#include "sasal.h"
|
||||
#include "environment.h"
|
||||
#include "version.h"
|
||||
#include "log_generator.h"
|
||||
#include "orchestration_comp.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace ReportIS;
|
||||
|
||||
SASAL_START // Orchestration - Manifest Handler
|
||||
|
||||
USE_DEBUG_FLAG(D_ORCHESTRATOR);
|
||||
|
||||
class IgnoredPackages
|
||||
{
|
||||
public:
|
||||
void
|
||||
load(istream &input, char delim)
|
||||
{
|
||||
string ignored_package;
|
||||
while (getline(input, ignored_package, delim))
|
||||
{
|
||||
if (ignored_package == "all") {
|
||||
ignore_packages.clear();
|
||||
ignore_packages.insert(ignored_package);
|
||||
dbgInfo(D_ORCHESTRATOR) << "Will ignore updates for all packages";
|
||||
break;
|
||||
} else if (ignored_package == "none") {
|
||||
ignore_packages.clear();
|
||||
dbgInfo(D_ORCHESTRATOR) << "Will not ignore updates of any packages";
|
||||
break;
|
||||
}
|
||||
|
||||
if (ignored_package.size() > 0) {
|
||||
ignore_packages.insert(ignored_package);
|
||||
dbgInfo(D_ORCHESTRATOR) << "Updates for package " << ignored_package << " will be ignored";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
load(const string &raw_value)
|
||||
{
|
||||
string token;
|
||||
istringstream tokenStream(raw_value);
|
||||
load(tokenStream, ',');
|
||||
}
|
||||
|
||||
const set<string> & operator*() const { return ignore_packages; }
|
||||
|
||||
private:
|
||||
set<string> ignore_packages;
|
||||
};
|
||||
|
||||
class ManifestController::Impl : Singleton::Provide<I_ManifestController>::From<ManifestController>
|
||||
{
|
||||
public:
|
||||
void init();
|
||||
|
||||
bool updateManifest(const string &new_manifest_file) override;
|
||||
bool loadAfterSelfUpdate() override;
|
||||
|
||||
private:
|
||||
bool changeManifestFile(const string &new_manifest_file);
|
||||
|
||||
bool
|
||||
handlePackage(
|
||||
const Package &updated_package,
|
||||
map<string, Package> ¤t_packages,
|
||||
const map<string, Package> &new_packages,
|
||||
map<string, Package> &corrupted_packages
|
||||
);
|
||||
|
||||
ManifestDiffCalculator manifest_diff_calc;
|
||||
ManifestHandler manifest_handler;
|
||||
|
||||
string manifest_file_path;
|
||||
string corrupted_file_list;
|
||||
string temp_ext;
|
||||
string backup_ext;
|
||||
string packages_dir;
|
||||
string orch_service_name;
|
||||
set<string> ignore_packages;
|
||||
};
|
||||
|
||||
void
|
||||
ManifestController::Impl::init()
|
||||
{
|
||||
manifest_diff_calc.init();
|
||||
manifest_handler.init();
|
||||
|
||||
dbgTrace(D_ORCHESTRATOR) << "Manifest controller, file system path prefix: " << getFilesystemPathConfig();
|
||||
|
||||
manifest_file_path = getConfigurationWithDefault<string>(
|
||||
getFilesystemPathConfig() + "/conf/manifest.json",
|
||||
"orchestration",
|
||||
"Manifest file path"
|
||||
);
|
||||
corrupted_file_list = getConfigurationWithDefault<string>(
|
||||
getFilesystemPathConfig() + "/conf/corrupted_packages.json",
|
||||
"orchestration",
|
||||
"Manifest corrupted files path"
|
||||
);
|
||||
temp_ext = getConfigurationWithDefault<string>("_temp", "orchestration", "Temp file extension");
|
||||
backup_ext = getConfigurationWithDefault<string>(".bk", "orchestration", "Backup file extension");
|
||||
packages_dir = getConfigurationWithDefault<string>(
|
||||
getFilesystemPathConfig() + "/packages",
|
||||
"orchestration",
|
||||
"Packages directory"
|
||||
);
|
||||
orch_service_name = getConfigurationWithDefault<string>("orchestration", "orchestration", "Service name");
|
||||
|
||||
auto ignore_packages_path = getConfigurationWithDefault<string>(
|
||||
getFilesystemPathConfig() + "/conf/ignore-packages.txt",
|
||||
"orchestration",
|
||||
"Ignore packages list file path"
|
||||
);
|
||||
|
||||
if (Singleton::Consume<I_OrchestrationTools>::by<ManifestController>()->doesFileExist(ignore_packages_path)) {
|
||||
try {
|
||||
ifstream input_stream(ignore_packages_path);
|
||||
if (!input_stream) {
|
||||
dbgWarning(D_ORCHESTRATOR)
|
||||
<< "Cannot open the file with ignored packages. "
|
||||
<< "File: " << ignore_packages_path;
|
||||
} else {
|
||||
IgnoredPackages packages_to_ignore;
|
||||
packages_to_ignore.load(input_stream, '\n');
|
||||
ignore_packages = *packages_to_ignore;
|
||||
|
||||
input_stream.close();
|
||||
}
|
||||
} catch (ifstream::failure &f) {
|
||||
dbgWarning(D_ORCHESTRATOR)
|
||||
<< "Cannot read the file with ignored packages."
|
||||
<< " File: " << ignore_packages_path
|
||||
<< " Error: " << f.what();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
ManifestController::Impl::updateManifest(const string &new_manifest_file)
|
||||
{
|
||||
auto i_env = Singleton::Consume<I_Environment>::by<ManifestController>();
|
||||
auto span_scope = i_env->startNewSpanScope(Span::ContextType::CHILD_OF);
|
||||
|
||||
dbgDebug(D_ORCHESTRATOR) << "Starting to update manifest file";
|
||||
auto ignored_settings_packages = getProfileAgentSetting<IgnoredPackages>("orchestration.IgnoredPackagesList");
|
||||
set<string> packages_to_ignore = ignore_packages;
|
||||
if (ignored_settings_packages.ok()) packages_to_ignore = *(*ignored_settings_packages);
|
||||
|
||||
auto orchestration_tools = Singleton::Consume<I_OrchestrationTools>::by<ManifestController>();
|
||||
|
||||
if (packages_to_ignore.count("all") > 0) {
|
||||
dbgTrace(D_ORCHESTRATOR) << "Nothing to update (\"ignore all\" turned on)";
|
||||
|
||||
if (!orchestration_tools->copyFile(new_manifest_file, manifest_file_path)) {
|
||||
dbgWarning(D_ORCHESTRATOR) << "Failed to copy a new manifest file";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Maybe<map<string, Package>> parsed_manifest = orchestration_tools->loadPackagesFromJson(new_manifest_file);
|
||||
if (!parsed_manifest.ok()) {
|
||||
dbgWarning(D_ORCHESTRATOR) << "Failed to parse the new manifest file. File: " << new_manifest_file;
|
||||
return false;
|
||||
}
|
||||
|
||||
map<string, Package> new_packages = parsed_manifest.unpack();
|
||||
map<string, Package> current_packages;
|
||||
parsed_manifest = orchestration_tools->loadPackagesFromJson(manifest_file_path);
|
||||
|
||||
if (!parsed_manifest.ok()){
|
||||
dbgWarning(D_ORCHESTRATOR) << "Can not parse the current manifest file, start with new one.";
|
||||
} else {
|
||||
current_packages = parsed_manifest.unpack();
|
||||
}
|
||||
|
||||
// Remove any update of all ignore packages
|
||||
for (const auto &ignore_package : packages_to_ignore) {
|
||||
dbgInfo(D_ORCHESTRATOR) << "Ignoring a package from the manifest. Package name: " << ignore_package;
|
||||
if (new_packages.count(ignore_package) > 0) {
|
||||
// Get the change as-is of the ignore package - it won"t update the service
|
||||
current_packages[ignore_package] = new_packages[ignore_package];
|
||||
} else {
|
||||
// Remove the ignore package from the current manifest file - it won't uninstall the service
|
||||
current_packages.erase(ignore_package);
|
||||
}
|
||||
}
|
||||
|
||||
map<string, Package> corrupted_packages;
|
||||
parsed_manifest = orchestration_tools->loadPackagesFromJson(corrupted_file_list);
|
||||
|
||||
if (!parsed_manifest.ok()){
|
||||
dbgWarning(D_ORCHESTRATOR) << "Can not parse corrupted services file, start with new one.";
|
||||
} else {
|
||||
corrupted_packages = parsed_manifest.unpack();
|
||||
}
|
||||
|
||||
bool all_cleaned = true;
|
||||
bool uninstall_done = false;
|
||||
// Removes all the untracked packages. new_packages will be cleaned from already installed packages
|
||||
auto packages_to_remove = manifest_diff_calc.filterUntrackedPackages(current_packages, new_packages);
|
||||
for (auto remove_package = packages_to_remove.begin(); remove_package != packages_to_remove.end();) {
|
||||
bool uninstall_response = true;
|
||||
if (remove_package->second.isInstallable().ok()) {
|
||||
uninstall_response = manifest_handler.uninstallPackage(remove_package->second);
|
||||
}
|
||||
|
||||
if (!uninstall_response) {
|
||||
dbgWarning(D_ORCHESTRATOR)
|
||||
<< "Failed to uninstall package. Package: " << remove_package->second.getName();
|
||||
all_cleaned = false;
|
||||
remove_package++;
|
||||
} else {
|
||||
uninstall_done = true;
|
||||
current_packages.erase(remove_package->first);
|
||||
remove_package = packages_to_remove.erase(remove_package);
|
||||
}
|
||||
}
|
||||
|
||||
if (uninstall_done) {
|
||||
if (!orchestration_tools->packagesToJsonFile(current_packages, manifest_file_path)) {
|
||||
dbgWarning(D_ORCHESTRATOR) << "Failed to update manifest file. File: "
|
||||
<< manifest_file_path;
|
||||
} else {
|
||||
dbgInfo(D_ORCHESTRATOR) << "Manifest file was updated successfully. File: "
|
||||
<< manifest_file_path;
|
||||
}
|
||||
}
|
||||
|
||||
bool no_change = new_packages.size() == 0;
|
||||
// Both new_packages & corrupted_packages will be updated based on updated manifest
|
||||
bool no_corrupted_package = manifest_diff_calc.filterCorruptedPackages(new_packages, corrupted_packages);
|
||||
|
||||
auto orchestration_service = new_packages.find("orchestration");
|
||||
if (orchestration_service != new_packages.end()) {
|
||||
// Orchestration needs special handling as manifest should be backup differently
|
||||
return handlePackage(
|
||||
orchestration_service->second,
|
||||
current_packages,
|
||||
new_packages,
|
||||
corrupted_packages
|
||||
);
|
||||
}
|
||||
auto wlp_standalone_service = new_packages.find("wlpStandalone");
|
||||
if (wlp_standalone_service != new_packages.end()) {
|
||||
// wlpStandalone needs special handling as manifest should be backup differently
|
||||
return handlePackage(
|
||||
wlp_standalone_service->second,
|
||||
current_packages,
|
||||
new_packages,
|
||||
corrupted_packages
|
||||
);
|
||||
}
|
||||
|
||||
bool all_installed = true;
|
||||
bool any_installed = false;
|
||||
|
||||
dbgDebug(D_ORCHESTRATOR) << "Starting to handle " << new_packages.size() <<" new packages";
|
||||
for (auto &new_package : new_packages) {
|
||||
|
||||
if (new_package.second.getType() != Package::PackageType::Service) continue;
|
||||
|
||||
size_t prev_size = corrupted_packages.size();
|
||||
bool handling_response = handlePackage(
|
||||
new_package.second,
|
||||
current_packages,
|
||||
new_packages,
|
||||
corrupted_packages
|
||||
);
|
||||
|
||||
// During handlePackage function, package installation might fail so it will be added to
|
||||
// corrupted_packages. Corrupted file needs to be updated accordingly
|
||||
if (prev_size < corrupted_packages.size() &&
|
||||
!orchestration_tools->packagesToJsonFile(corrupted_packages, corrupted_file_list)) {
|
||||
dbgWarning(D_ORCHESTRATOR) << "Failed to update corrupted packages list.";
|
||||
}
|
||||
|
||||
// Orchestration needs special handling as manifest should be backup differently
|
||||
if (new_package.first.compare(orch_service_name) == 0) {
|
||||
return handling_response;
|
||||
}
|
||||
|
||||
any_installed = any_installed || handling_response;
|
||||
all_installed = all_installed && handling_response;
|
||||
}
|
||||
|
||||
bool manifest_file_update = true;
|
||||
|
||||
if (all_installed && (any_installed || no_change) && no_corrupted_package) {
|
||||
manifest_file_update = changeManifestFile(new_manifest_file);
|
||||
} else if (any_installed) {
|
||||
manifest_file_update = orchestration_tools->packagesToJsonFile(current_packages, manifest_file_path);
|
||||
}
|
||||
return all_installed && manifest_file_update && no_corrupted_package && all_cleaned;
|
||||
}
|
||||
|
||||
// Orchestration package needs a special handling. Old service will die during the upgrade
|
||||
// so we need to keep temporary manifest file to prevent overwriting. Once Orchestration upgrade
|
||||
// finish, we return to regular path.
|
||||
bool
|
||||
ManifestController::Impl::loadAfterSelfUpdate()
|
||||
{
|
||||
dbgDebug(D_ORCHESTRATOR) << "Starting load after the self update function";
|
||||
string temp_manifest_path = manifest_file_path + temp_ext;
|
||||
auto orchestration_tools = Singleton::Consume<I_OrchestrationTools>::by<ManifestController>();
|
||||
if (!orchestration_tools->doesFileExist(temp_manifest_path)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
dbgDebug(D_ORCHESTRATOR) << "Orchestration updated itself";
|
||||
// Run post installation test
|
||||
auto package_handler = Singleton::Consume<I_PackageHandler>::by<ManifestController>();
|
||||
string current_file = packages_dir + "/" + orch_service_name + "/" + orch_service_name;
|
||||
if (!package_handler->postInstallPackage(orch_service_name, current_file + temp_ext)) {
|
||||
dbgWarning(D_ORCHESTRATOR) << "Failed in post install test. Package: " << orch_service_name;
|
||||
return false;
|
||||
}
|
||||
dbgDebug(D_ORCHESTRATOR) << "Post installation test for the self update package succeed";
|
||||
|
||||
if (!changeManifestFile(temp_manifest_path)) {
|
||||
dbgWarning(D_ORCHESTRATOR) << "Failed to change manifest file after update the orchestration service.";
|
||||
return false;
|
||||
}
|
||||
dbgDebug(D_ORCHESTRATOR) << "Update the temporary manifest to be the running manifest";
|
||||
|
||||
string backup_file = current_file + backup_ext;
|
||||
string backup_temp_file = backup_file + temp_ext;
|
||||
|
||||
if (!package_handler->updateSavedPackage(orch_service_name, current_file + temp_ext)) {
|
||||
dbgWarning(D_ORCHESTRATOR) << "Failed to update the saved package. Package: " << orch_service_name;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ManifestController::Impl::changeManifestFile(const string &new_manifest_file)
|
||||
{
|
||||
dbgDebug(D_ORCHESTRATOR) << "Backup the old manifest file";
|
||||
auto orchestration_tools = Singleton::Consume<I_OrchestrationTools>::by<ManifestController>();
|
||||
|
||||
if (orchestration_tools->doesFileExist(manifest_file_path)) {
|
||||
if (!orchestration_tools->copyFile(manifest_file_path,
|
||||
manifest_file_path + backup_ext)) {
|
||||
dbgWarning(D_ORCHESTRATOR) << "Failed to backup the old manifest file";
|
||||
}
|
||||
}
|
||||
|
||||
dbgDebug(D_ORCHESTRATOR) << "Writing new manifest to file";
|
||||
if (!orchestration_tools->copyFile(new_manifest_file, manifest_file_path)) {
|
||||
dbgWarning(D_ORCHESTRATOR) << "Failed write new manifest to file";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!orchestration_tools->isNonEmptyFile(manifest_file_path)) {
|
||||
dbgWarning(D_ORCHESTRATOR) << "Failed to get manifest file data";
|
||||
return false;
|
||||
}
|
||||
|
||||
dbgInfo(D_ORCHESTRATOR) << "Manifest file has been updated.";
|
||||
|
||||
if (!orchestration_tools->removeFile(new_manifest_file)) {
|
||||
dbgWarning(D_ORCHESTRATOR) << "Failed to remove new manifest file. Path: " << new_manifest_file;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ManifestController::Impl::handlePackage(
|
||||
const Package &package,
|
||||
map<string, Package> ¤t_packages,
|
||||
const map<string, Package> &new_packages,
|
||||
map<string, Package> &corrupted_packages)
|
||||
{
|
||||
auto i_env = Singleton::Consume<I_Environment>::by<ManifestController>();
|
||||
auto span_scope = i_env->startNewSpanScope(Span::ContextType::CHILD_OF);
|
||||
dbgDebug(D_ORCHESTRATOR) << "Handling package. Package: " << package.getName();
|
||||
|
||||
if (!package.isInstallable().ok()) {
|
||||
string report_msg =
|
||||
"Skipping installation of " + package.getName() + ". Reason: " + package.isInstallable().getErr();
|
||||
dbgWarning(D_ORCHESTRATOR) << report_msg;
|
||||
LogGen(report_msg, Audience::SECURITY, Severity::CRITICAL, Priority::HIGH, Tags::ORCHESTRATOR);
|
||||
current_packages.insert(make_pair(package.getName(), package));
|
||||
return true;
|
||||
}
|
||||
|
||||
vector<Package> installation_queue;
|
||||
|
||||
if (!manifest_diff_calc.buildInstallationQueue(package, installation_queue, current_packages, new_packages)) {
|
||||
dbgWarning(D_ORCHESTRATOR) << "Failed building installation queue. Package: " << package.getName();
|
||||
return false;
|
||||
}
|
||||
|
||||
vector<pair<Package, string>> downloaded_files;
|
||||
|
||||
if (!manifest_handler.downloadPackages(installation_queue, downloaded_files)) return false;
|
||||
if (!manifest_handler.installPackages(downloaded_files, current_packages, corrupted_packages)) {
|
||||
LogGen(
|
||||
"Failed to install package: " + package.getName(),
|
||||
Audience::SECURITY,
|
||||
Severity::CRITICAL,
|
||||
Priority::HIGH,
|
||||
Tags::ORCHESTRATOR
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
dbgInfo(D_ORCHESTRATOR) << "Package was installed successfully. Package: " << package.getName();
|
||||
return true;
|
||||
}
|
||||
|
||||
ManifestController::ManifestController() : Component("ManifestController"), pimpl(make_unique<Impl>()) {}
|
||||
|
||||
ManifestController::~ManifestController() {}
|
||||
|
||||
void
|
||||
ManifestController::init()
|
||||
{
|
||||
pimpl->init();
|
||||
}
|
||||
|
||||
SASAL_END
|
||||
@@ -0,0 +1,7 @@
|
||||
link_directories(${BOOST_ROOT}/lib)
|
||||
|
||||
add_unit_test(
|
||||
manifest_controller_ut
|
||||
"manifest_controller_ut.cc"
|
||||
"manifest_controller;logging;orchestration_modules;agent_details;agent_details_reporter;version;config;metric;event_is;-lboost_regex"
|
||||
)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,144 @@
|
||||
// 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 "manifest_diff_calculator.h"
|
||||
|
||||
#include "debug.h"
|
||||
#include "config.h"
|
||||
#include "sasal.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
SASAL_START // Orchestration - Manifest Handler
|
||||
|
||||
USE_DEBUG_FLAG(D_ORCHESTRATOR);
|
||||
|
||||
void
|
||||
ManifestDiffCalculator::init()
|
||||
{
|
||||
dbgTrace(D_ORCHESTRATOR)
|
||||
<< "Initializing Manifest diff calculator, file system path prefix:: "
|
||||
<< getFilesystemPathConfig();
|
||||
|
||||
corrupted_file_path = getConfigurationWithDefault<string>(
|
||||
getFilesystemPathConfig() + "/conf/corrupted_packages.json",
|
||||
"orchestration",
|
||||
"Manifest corrupted files path"
|
||||
);
|
||||
}
|
||||
|
||||
// If one of the new packages is already installed, new_packages map is updated accordingly.
|
||||
// This function return map<string, Package> which contain all packages that should be uninstalled
|
||||
// based on new manifest
|
||||
map<string, Package>
|
||||
ManifestDiffCalculator::filterUntrackedPackages(
|
||||
const map<string, Package> ¤t_packages,
|
||||
map<string, Package> &new_packages)
|
||||
{
|
||||
dbgDebug(D_ORCHESTRATOR) << "Starting to scan old packages to remove";
|
||||
map<string, Package> packages_to_remove;
|
||||
for (auto current_package = current_packages.begin(); current_package != current_packages.end();) {
|
||||
auto package = new_packages.find(current_package->first);
|
||||
if (package == new_packages.end()) {
|
||||
packages_to_remove.insert(pair<string, Package>(current_package->first, current_package->second));
|
||||
} else {
|
||||
if (current_package->second == package->second) {
|
||||
// if package is already installed, new_packages is updated
|
||||
new_packages.erase(package);
|
||||
}
|
||||
}
|
||||
current_package++;
|
||||
}
|
||||
return packages_to_remove;
|
||||
}
|
||||
|
||||
// If one of the new packages is already known as corrupted, new_packages map is
|
||||
// updated accordingly.
|
||||
// Otherwise, corrupted_packages is updated and old corrupted package is deleted.
|
||||
bool
|
||||
ManifestDiffCalculator::filterCorruptedPackages(
|
||||
map<string, Package> &new_packages,
|
||||
map<string, Package> &corrupted_packages)
|
||||
{
|
||||
bool no_corrupted_package_exist = true;
|
||||
bool any_corrupted_removed = false;
|
||||
for (auto corrupted_package = corrupted_packages.begin(); corrupted_package != corrupted_packages.end();) {
|
||||
auto package = new_packages.find(corrupted_package->first);
|
||||
if (package == new_packages.end()) {
|
||||
// The corrupted package is not in the new packages list,
|
||||
// so it should be removed from the corrupted list.
|
||||
corrupted_package = corrupted_packages.erase(corrupted_package);
|
||||
any_corrupted_removed = true;
|
||||
} else {
|
||||
if (corrupted_package->second == package->second) {
|
||||
// The corrupted package is still in the new packages list,
|
||||
// so it should be removed
|
||||
dbgWarning(D_ORCHESTRATOR) << "Installation package is corrupted."
|
||||
<< " Package: " << package->second.getName();
|
||||
new_packages.erase(package);
|
||||
corrupted_package++;
|
||||
no_corrupted_package_exist = false;
|
||||
} else {
|
||||
// New version of corrupted package was received
|
||||
corrupted_package = corrupted_packages.erase(corrupted_package);
|
||||
any_corrupted_removed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (any_corrupted_removed) {
|
||||
dbgDebug(D_ORCHESTRATOR) << "Updating corrupted file. File: " << corrupted_file_path;
|
||||
auto orchestration_tools = Singleton::Consume<I_OrchestrationTools>::by<ManifestDiffCalculator>();
|
||||
if (!orchestration_tools->packagesToJsonFile(corrupted_packages, corrupted_file_path)) {
|
||||
dbgWarning(D_ORCHESTRATOR) << "Failed to update corrupted file. Path: " << corrupted_file_path;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return no_corrupted_package_exist;
|
||||
}
|
||||
|
||||
// This function build the installation queue recursively and return true if succeeded, false otherwise
|
||||
// At the beginning, installation_queue is empty and will be filled according package dependences
|
||||
bool
|
||||
ManifestDiffCalculator::buildInstallationQueue(
|
||||
const Package &updated_package,
|
||||
vector<Package> &installation_queue,
|
||||
const map<string, Package> ¤t_packages,
|
||||
const map<string, Package> &new_packages)
|
||||
{
|
||||
vector<string> requires = updated_package.getRequire();
|
||||
|
||||
for (size_t i = 0; i < requires.size(); i++) {
|
||||
auto installed_package = current_packages.find(requires[i]);
|
||||
auto new_package = new_packages.find(requires[i]);
|
||||
|
||||
if (installed_package == current_packages.end() ||
|
||||
(new_package != new_packages.end() && *installed_package != *new_package)) {
|
||||
if(!buildInstallationQueue(new_package->second,
|
||||
installation_queue,
|
||||
current_packages,
|
||||
new_packages)) {
|
||||
return false;
|
||||
}
|
||||
} else if (installed_package != current_packages.end()) {
|
||||
dbgDebug(D_ORCHESTRATOR) << "Package is already installed. Package: " << installed_package->first;
|
||||
} else if (new_package == new_packages.end()) {
|
||||
dbgWarning(D_ORCHESTRATOR) << "One of the requested dependencies is corrupted or doesn't exist."
|
||||
<< " Package: "<< requires[i];
|
||||
return false;
|
||||
}
|
||||
}
|
||||
installation_queue.push_back(updated_package);
|
||||
return true;
|
||||
}
|
||||
|
||||
SASAL_END
|
||||
384
components/security_apps/orchestration/manifest_controller/manifest_handler.cc
Executable file
384
components/security_apps/orchestration/manifest_controller/manifest_handler.cc
Executable file
@@ -0,0 +1,384 @@
|
||||
// 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 "manifest_handler.h"
|
||||
|
||||
#include "debug.h"
|
||||
#include "config.h"
|
||||
#include "sasal.h"
|
||||
#include "agent_details.h"
|
||||
#include "orchestration_comp.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
SASAL_START // Orchestration - Manifest Handler
|
||||
|
||||
USE_DEBUG_FLAG(D_ORCHESTRATOR);
|
||||
|
||||
void
|
||||
ManifestHandler::init()
|
||||
{
|
||||
dbgTrace(D_ORCHESTRATOR)
|
||||
<< "Initializing Manifest handler, file system path prefix: "
|
||||
<< getFilesystemPathConfig();
|
||||
|
||||
manifest_file_path = getConfigurationWithDefault<string>(
|
||||
getFilesystemPathConfig() + "/conf/manifest.json",
|
||||
"orchestration",
|
||||
"Manifest file path"
|
||||
);
|
||||
temp_ext = getConfigurationWithDefault<string>("_temp", "orchestration", "Temp file extension");
|
||||
backup_ext = getConfigurationWithDefault<string>(".bk", "orchestration", "Backup file extension");
|
||||
packages_dir = getConfigurationWithDefault<string>(
|
||||
getFilesystemPathConfig() + "/packages", "orchestration",
|
||||
"Packages directory"
|
||||
);
|
||||
orch_service_name = getConfigurationWithDefault<string>("orchestration", "orchestration", "Service name");
|
||||
default_dir = getConfigurationWithDefault<string>(
|
||||
getFilesystemPathConfig(),
|
||||
"orchestration",
|
||||
"Default Check Point directory"
|
||||
);
|
||||
}
|
||||
|
||||
Maybe<string>
|
||||
ManifestHandler::downloadPackage(const Package &package, bool is_clean_installation)
|
||||
{
|
||||
Maybe<string> package_download_file = genError("failed to download package, Package: " + package.getName());
|
||||
Maybe<string> fog_domain = genError("No Fog domain was found");
|
||||
if (Singleton::exists<I_AgentDetails>()) {
|
||||
fog_domain = Singleton::Consume<I_AgentDetails>::by<ManifestHandler>()->getFogDomain();
|
||||
}
|
||||
|
||||
if (!is_clean_installation) {
|
||||
I_MainLoop *i_mainloop = Singleton::Consume<I_MainLoop>::by<ManifestHandler>();
|
||||
auto pending_time_frame_seconds = getConfigurationWithDefault<int>(
|
||||
60,
|
||||
"orchestration",
|
||||
"Download pending time frame seconds"
|
||||
);
|
||||
int pending_time = rand() % pending_time_frame_seconds;
|
||||
dbgInfo(D_ORCHESTRATOR)
|
||||
<< "Pending downloading of package "
|
||||
<< package.getName()
|
||||
<< " for "
|
||||
<< pending_time
|
||||
<< " seconds";
|
||||
chrono::microseconds pending_time_micro = chrono::seconds(pending_time);
|
||||
i_mainloop->yield(pending_time_micro);
|
||||
dbgTrace(D_ORCHESTRATOR) << "Proceeding to package downloading. Package name " << package.getName();
|
||||
}
|
||||
|
||||
auto orchestration_downloader = Singleton::Consume<I_Downloader>::by<ManifestHandler>();
|
||||
if (!package.getRelativeDownloadPath().empty() && fog_domain.ok()) {
|
||||
string download_path =
|
||||
"<JWT>https://" + fog_domain.unpack() + "/download" + package.getRelativeDownloadPath();
|
||||
package_download_file= orchestration_downloader->downloadFileFromURL(
|
||||
download_path,
|
||||
package.getChecksum(),
|
||||
package.getChecksumType(),
|
||||
package.getName()
|
||||
);
|
||||
}
|
||||
|
||||
if (!package_download_file.ok()) {
|
||||
package_download_file = orchestration_downloader->downloadFileFromURL(
|
||||
package.getDownloadPath(),
|
||||
package.getChecksum(),
|
||||
package.getChecksumType(),
|
||||
package.getName()
|
||||
);
|
||||
}
|
||||
return package_download_file;
|
||||
}
|
||||
|
||||
bool
|
||||
ManifestHandler::downloadPackages(
|
||||
const vector<Package> &packages_to_download,
|
||||
vector<pair<Package, packageFilePath>> &downloaded_packages)
|
||||
{
|
||||
auto i_env = Singleton::Consume<I_Environment>::by<ManifestHandler>();
|
||||
auto i_orch_tools = Singleton::Consume<I_OrchestrationTools>::by<ManifestHandler>();
|
||||
auto span_scope = i_env->startNewSpanScope(Span::ContextType::CHILD_OF);
|
||||
for (auto &package : packages_to_download) {
|
||||
dbgInfo(D_ORCHESTRATOR) << "Downloading package file." << " Package: " << package.getName();
|
||||
|
||||
string packages_dir = getConfigurationWithDefault<string>(
|
||||
"/etc/cp/packages",
|
||||
"orchestration",
|
||||
"Packages directory"
|
||||
);
|
||||
|
||||
string current_installation_file = packages_dir + "/" + package.getName() + "/" + package.getName();
|
||||
bool is_clean_installation = !i_orch_tools->doesFileExist(current_installation_file);
|
||||
|
||||
Maybe<string> package_download_file = downloadPackage(package, is_clean_installation);
|
||||
|
||||
if (package_download_file.ok()) {
|
||||
dbgDebug(D_ORCHESTRATOR)
|
||||
<< "Installation package was downloaded successfully."
|
||||
<< " Package: " << package.getName();
|
||||
downloaded_packages.push_back(pair<Package, packageFilePath>(package, package_download_file.unpack()));
|
||||
} else {
|
||||
dbgWarning(D_ORCHESTRATOR)
|
||||
<< "Failed to download installation package. "
|
||||
<< "Package: " << package.getName()
|
||||
<< ", Error: " << package_download_file.getErr();
|
||||
|
||||
for (auto &package_file : downloaded_packages) {
|
||||
if (i_orch_tools->removeFile(package_file.second)) {
|
||||
dbgDebug(D_ORCHESTRATOR) << "Corrupted downloaded package was removed. Package: "
|
||||
<< package_file.first.getName();
|
||||
} else {
|
||||
dbgWarning(D_ORCHESTRATOR)
|
||||
<< "Failed to removed the download file. Package: "
|
||||
<< package_file.first.getName()
|
||||
<< ", Path: "
|
||||
<< package_file.second;
|
||||
}
|
||||
}
|
||||
downloaded_packages.clear();
|
||||
string install_error;
|
||||
if (is_clean_installation) {
|
||||
string error_hostname_addition = "";
|
||||
auto maybe_hostname = Singleton::Consume<I_DetailsResolver>::by<ManifestHandler>()->getHostname();
|
||||
if (maybe_hostname.ok()) {
|
||||
error_hostname_addition = " on host '" + maybe_hostname.unpack() + "'";
|
||||
}
|
||||
install_error =
|
||||
"Critical Error: Agent/Gateway was not fully deployed" +
|
||||
error_hostname_addition +
|
||||
" and is not enforcing a security policy. Retry installation or contact Check Point support.";
|
||||
} else {
|
||||
auto agent_details = Singleton::Consume<I_AgentDetails>::by<ManifestHandler>();
|
||||
install_error =
|
||||
"Warning: Agent/Gateway '" +
|
||||
agent_details->getAgentId() +
|
||||
"' software update failed. Agent is running previous software. Contact Check Point support.";
|
||||
}
|
||||
|
||||
auto orchestration_status = Singleton::Consume<I_OrchestrationStatus>::by<ManifestHandler>();
|
||||
if (orchestration_status->getManifestError().find("Gateway was not fully deployed") == string::npos) {
|
||||
orchestration_status->setFieldStatus(
|
||||
OrchestrationStatusFieldType::MANIFEST,
|
||||
OrchestrationStatusResult::FAILED,
|
||||
install_error
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ManifestHandler::installPackages(
|
||||
const vector<pair<Package, packageFilePath>> &downloaded_package_files,
|
||||
map<packageFilePath, Package> ¤t_packages,
|
||||
map<packageFilePath, Package> &corrupted_packages)
|
||||
{
|
||||
auto i_env = Singleton::Consume<I_Environment>::by<ManifestHandler>();
|
||||
auto span_scope = i_env->startNewSpanScope(Span::ContextType::CHILD_OF);
|
||||
// Patch - reorder packages so that accessControlApp is installed before accessControlKernel
|
||||
vector<pair<Package, packageFilePath>> patched_downloaded_package_files;
|
||||
patched_downloaded_package_files.reserve(downloaded_package_files.size());
|
||||
int ac_kernel_package_idx = -1;
|
||||
int ac_app_package_idx = -1;
|
||||
int i = 0;
|
||||
for (auto &downloaded_package : downloaded_package_files) {
|
||||
if (downloaded_package.first.getName() == "accessControlApp") {
|
||||
ac_app_package_idx = i;
|
||||
} else if (downloaded_package.first.getName() == "accessControlKernel") {
|
||||
ac_kernel_package_idx = i;
|
||||
} else {
|
||||
patched_downloaded_package_files.push_back(downloaded_package);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (ac_app_package_idx != -1) {
|
||||
patched_downloaded_package_files.push_back(downloaded_package_files.at(ac_app_package_idx));
|
||||
}
|
||||
if (ac_kernel_package_idx != -1) {
|
||||
patched_downloaded_package_files.push_back(downloaded_package_files.at(ac_kernel_package_idx));
|
||||
}
|
||||
|
||||
auto orchestration_status = Singleton::Consume<I_OrchestrationStatus>::by<ManifestHandler>();
|
||||
for (auto &downloaded_package : patched_downloaded_package_files) {
|
||||
auto package = downloaded_package.first;
|
||||
auto package_name = package.getName();
|
||||
auto package_handler_path = downloaded_package.second;
|
||||
|
||||
dbgInfo(D_ORCHESTRATOR) << "Handling package installation. Package: " << package_name;
|
||||
|
||||
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 agent_details = Singleton::Consume<I_AgentDetails>::by<ManifestHandler>();
|
||||
string install_error =
|
||||
"Warning: Agent/Gateway '" +
|
||||
agent_details->getAgentId() +
|
||||
"' software update failed. Agent is running previous software. Contact Check Point support.";
|
||||
if (orchestration_status->getManifestError().find("Gateway was not fully deployed") == string::npos) {
|
||||
orchestration_status->setFieldStatus(
|
||||
OrchestrationStatusFieldType::MANIFEST,
|
||||
OrchestrationStatusResult::FAILED,
|
||||
install_error
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return self_update_status;
|
||||
}
|
||||
|
||||
string packages_dir = getConfigurationWithDefault<string>(
|
||||
"/etc/cp/packages",
|
||||
"orchestration",
|
||||
"Packages directory"
|
||||
);
|
||||
|
||||
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);
|
||||
|
||||
auto package_handler = Singleton::Consume<I_PackageHandler>::by<ManifestHandler>();
|
||||
if (!package_handler->shouldInstallPackage(package_name, package_handler_path)) {
|
||||
current_packages.insert(make_pair(package_name, package));
|
||||
dbgInfo(D_ORCHESTRATOR)
|
||||
<< "Skipping installation of new package with the same version as current. Package: "
|
||||
<< package_name;
|
||||
continue;
|
||||
}
|
||||
|
||||
bool current_result = true;
|
||||
bool is_service = package.getType() == Package::PackageType::Service;
|
||||
if (is_service) {
|
||||
current_result = package_handler->preInstallPackage(package_name, package_handler_path);
|
||||
}
|
||||
|
||||
current_result = current_result && package_handler->installPackage(
|
||||
package_name,
|
||||
package_handler_path,
|
||||
false
|
||||
);
|
||||
|
||||
if (current_result && is_service) {
|
||||
current_result = package_handler->postInstallPackage(package_name, package_handler_path);
|
||||
}
|
||||
|
||||
if (current_result && is_service) {
|
||||
current_result = package_handler->updateSavedPackage(package_name, package_handler_path);
|
||||
}
|
||||
|
||||
if (!current_result) {
|
||||
string install_error;
|
||||
if (is_clean_installation) {
|
||||
string error_hostname_addition = "";
|
||||
auto maybe_hostname = Singleton::Consume<I_DetailsResolver>::by<ManifestHandler>()->getHostname();
|
||||
if (maybe_hostname.ok()) {
|
||||
error_hostname_addition = " on host '" + maybe_hostname.unpack() + "'";
|
||||
}
|
||||
install_error =
|
||||
"Critical Error: Agent/Gateway was not fully deployed" +
|
||||
error_hostname_addition +
|
||||
" and is not enforcing a security policy. Retry installation or contact Check Point support.";
|
||||
} else {
|
||||
auto agent_details = Singleton::Consume<I_AgentDetails>::by<ManifestHandler>();
|
||||
install_error =
|
||||
"Warning: Agent/Gateway '" +
|
||||
agent_details->getAgentId() +
|
||||
"' software update failed. Agent is running previous software. Contact Check Point support.";
|
||||
}
|
||||
corrupted_packages.insert(make_pair(package_name, package));
|
||||
dbgWarning(D_ORCHESTRATOR) << "Failed to install package. Package: " << package_name;
|
||||
|
||||
auto orchestration_status = Singleton::Consume<I_OrchestrationStatus>::by<ManifestHandler>();
|
||||
if (orchestration_status->getManifestError().find("Gateway was not fully deployed") == string::npos) {
|
||||
orchestration_status->setFieldStatus(
|
||||
OrchestrationStatusFieldType::MANIFEST,
|
||||
OrchestrationStatusResult::FAILED,
|
||||
install_error
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
current_packages.insert(make_pair(package_name, package));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ManifestHandler::uninstallPackage(Package &removed_package)
|
||||
{
|
||||
dbgDebug(D_ORCHESTRATOR) << "Starting uninstalling. Package: " << removed_package.getName();
|
||||
string package_name = removed_package.getName();
|
||||
string package_path = default_dir + "/" + package_name + "/" + package_name;
|
||||
string installation_package = packages_dir + "/" + package_name + "/" + package_name;
|
||||
auto package_handler = Singleton::Consume<I_PackageHandler>::by<ManifestHandler>();
|
||||
return package_handler->uninstallPackage(package_name, package_path, installation_package);
|
||||
}
|
||||
|
||||
bool
|
||||
ManifestHandler::selfUpdate(
|
||||
const Package &updated_package,
|
||||
map<packageFilePath, Package> ¤t_packages,
|
||||
const string &installation_file)
|
||||
{
|
||||
dbgInfo(D_ORCHESTRATOR) << "Updating orchestration service";
|
||||
|
||||
auto current_service = current_packages.find(updated_package.getName());
|
||||
if (current_service != current_packages.end()) {
|
||||
current_service->second = updated_package;
|
||||
} else {
|
||||
current_packages.insert(pair<packageFilePath, Package>(updated_package.getName(), updated_package));
|
||||
}
|
||||
|
||||
string temp_manifest_path = manifest_file_path + temp_ext;
|
||||
|
||||
auto orchestration_tools = Singleton::Consume<I_OrchestrationTools>::by<ManifestHandler>();
|
||||
if (!orchestration_tools->packagesToJsonFile(current_packages, temp_manifest_path)) {
|
||||
dbgWarning(D_ORCHESTRATOR) << "Updating manifest temporary file has failed. File: " << temp_manifest_path;
|
||||
return false;
|
||||
}
|
||||
|
||||
string current_file = packages_dir + "/" + orch_service_name + "/" + orch_service_name;
|
||||
string backup_file = current_file + backup_ext;
|
||||
|
||||
dbgDebug(D_ORCHESTRATOR) << "Saving the temporary backup file.";
|
||||
if (orchestration_tools->doesFileExist(current_file)) {
|
||||
dbgDebug(D_ORCHESTRATOR) << "Backup current installation package. Destination: " << backup_file;
|
||||
if (!orchestration_tools->copyFile(current_file, backup_file + temp_ext)) {
|
||||
dbgWarning(D_ORCHESTRATOR) << "Failed to backup installation file. File: " << current_file;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
dbgDebug(D_ORCHESTRATOR) << "There is no previous version for Orchestration";
|
||||
}
|
||||
|
||||
string current_installation_file = current_file + temp_ext;
|
||||
dbgDebug(D_ORCHESTRATOR) << "Saving the installation file: " << current_installation_file;
|
||||
if (!orchestration_tools->copyFile(installation_file, current_installation_file)) {
|
||||
dbgWarning(D_ORCHESTRATOR) << "Failed to save the installation file: " << current_installation_file;
|
||||
return false;
|
||||
}
|
||||
|
||||
dbgDebug(D_ORCHESTRATOR) << "Starting to install the orchestration: " << current_installation_file;
|
||||
|
||||
auto package_handler = Singleton::Consume<I_PackageHandler>::by<ManifestHandler>();
|
||||
return
|
||||
package_handler->preInstallPackage(orch_service_name, current_installation_file) &&
|
||||
package_handler->installPackage(orch_service_name, current_installation_file, false);
|
||||
}
|
||||
|
||||
SASAL_END
|
||||
10
components/security_apps/orchestration/modules/CMakeLists.txt
Executable file
10
components/security_apps/orchestration/modules/CMakeLists.txt
Executable file
@@ -0,0 +1,10 @@
|
||||
add_library(
|
||||
orchestration_modules
|
||||
orchestration_policy.cc
|
||||
url_parser.cc
|
||||
package.cc
|
||||
orchestration_status.cc
|
||||
data.cc
|
||||
)
|
||||
|
||||
add_subdirectory(modules_ut)
|
||||
52
components/security_apps/orchestration/modules/data.cc
Executable file
52
components/security_apps/orchestration/modules/data.cc
Executable file
@@ -0,0 +1,52 @@
|
||||
// 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 "orchestrator/data.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "sasal.h"
|
||||
|
||||
SASAL_START // Orchestration - Modules
|
||||
|
||||
using namespace std;
|
||||
using namespace cereal;
|
||||
|
||||
USE_DEBUG_FLAG(D_ORCHESTRATOR);
|
||||
|
||||
static const map<string, Data::ChecksumTypes> checksum_map = {
|
||||
{ "sha1sum", Data::ChecksumTypes::SHA1 },
|
||||
{ "sha256sum", Data::ChecksumTypes::SHA256 },
|
||||
{ "sha512sum", Data::ChecksumTypes::SHA512 },
|
||||
{ "md5sum", Data::ChecksumTypes::MD5 }
|
||||
};
|
||||
|
||||
void
|
||||
Data::serialize(JSONInputArchive &in_archive)
|
||||
{
|
||||
string checksum_type_as_string;
|
||||
in_archive(make_nvp("checksumType", checksum_type_as_string));
|
||||
if (checksum_map.find(checksum_type_as_string) != checksum_map.end()) {
|
||||
checksum_type = checksum_map.at(checksum_type_as_string);
|
||||
} else {
|
||||
dbgWarning(D_ORCHESTRATOR) << "Unsupported checksum type: " << checksum_type_as_string;
|
||||
return;
|
||||
}
|
||||
in_archive(
|
||||
make_nvp("downloadPath", download_path),
|
||||
make_nvp("checksum", checksum_value),
|
||||
make_nvp("version", version)
|
||||
);
|
||||
}
|
||||
|
||||
SASAL_END
|
||||
7
components/security_apps/orchestration/modules/modules_ut/CMakeLists.txt
Executable file
7
components/security_apps/orchestration/modules/modules_ut/CMakeLists.txt
Executable file
@@ -0,0 +1,7 @@
|
||||
link_directories(${BOOST_ROOT}/lib)
|
||||
|
||||
add_unit_test(
|
||||
orchestration_modules_ut
|
||||
"orchestration_policy_ut.cc;url_parser_ut.cc;package_ut.cc;orchestration_status_ut.cc;data_ut.cc;"
|
||||
"orchestration_modules;config;environment;metric;event_is;time_proxy;-lboost_regex"
|
||||
)
|
||||
85
components/security_apps/orchestration/modules/modules_ut/data_ut.cc
Executable file
85
components/security_apps/orchestration/modules/modules_ut/data_ut.cc
Executable file
@@ -0,0 +1,85 @@
|
||||
#include "orchestrator/data.h"
|
||||
|
||||
#include "cereal/types/string.hpp"
|
||||
#include "cereal/archives/json.hpp"
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <fstream>
|
||||
|
||||
#include "cptest.h"
|
||||
#include "customized_cereal_map.h"
|
||||
|
||||
using namespace testing;
|
||||
using namespace std;
|
||||
|
||||
class DataTest : public Test
|
||||
{
|
||||
public:
|
||||
bool
|
||||
load(stringstream &string_stream, Data &data)
|
||||
{
|
||||
try {
|
||||
cereal::JSONInputArchive archive_in(string_stream);
|
||||
data.serialize(archive_in);
|
||||
} catch (const cereal::Exception &) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(DataTest, doNothing)
|
||||
{
|
||||
}
|
||||
|
||||
TEST_F(DataTest, serializationFromString)
|
||||
{
|
||||
stringstream string_stream;
|
||||
string_stream << "{"
|
||||
" \"version\": \"c\","
|
||||
" \"downloadPath\": \"https://a/data.json\",\n"
|
||||
" \"checksumType\": \"sha1sum\","
|
||||
" \"checksum\": \"8d4a5709673a05b380ba7d6567e28910019118f5\""
|
||||
"}";
|
||||
bool res = false;
|
||||
Data data;
|
||||
try {
|
||||
cereal::JSONInputArchive archive_in(string_stream);
|
||||
data.serialize(archive_in);
|
||||
res = true;
|
||||
} catch (const cereal::Exception &) {
|
||||
}
|
||||
EXPECT_EQ(true, res);
|
||||
|
||||
EXPECT_EQ(Data::ChecksumTypes::SHA1, data.getChecksumType());
|
||||
EXPECT_EQ("8d4a5709673a05b380ba7d6567e28910019118f5", data.getChecksum());
|
||||
EXPECT_EQ("c", data.getVersion());
|
||||
EXPECT_EQ("https://a/data.json", data.getDownloadPath());
|
||||
}
|
||||
|
||||
TEST_F(DataTest, serializationFromStringAsMap)
|
||||
{
|
||||
stringstream string_stream;
|
||||
string_stream << "{\n"
|
||||
" \"ips\": {\n"
|
||||
" \"version\": \"c\","
|
||||
" \"downloadPath\": \"https://a/data.json\",\n"
|
||||
" \"checksumType\": \"sha1sum\","
|
||||
" \"checksum\": \"8d4a5709673a05b380ba7d6567e28910019118f5\""
|
||||
" }\n"
|
||||
"}\n";
|
||||
map<string, Data> data;
|
||||
bool res = false;
|
||||
try {
|
||||
cereal::JSONInputArchive archive_in(string_stream);
|
||||
cereal::load(archive_in, data);
|
||||
res = true;
|
||||
} catch (const cereal::Exception &e) {
|
||||
}
|
||||
EXPECT_EQ(true, res);
|
||||
|
||||
EXPECT_EQ(Data::ChecksumTypes::SHA1, data["ips"].getChecksumType());
|
||||
EXPECT_EQ("8d4a5709673a05b380ba7d6567e28910019118f5", data["ips"].getChecksum());
|
||||
EXPECT_EQ("c", data["ips"].getVersion());
|
||||
EXPECT_EQ("https://a/data.json", data["ips"].getDownloadPath());
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
#include "orchestration_policy.h"
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
#include "cptest.h"
|
||||
#include "cereal/types/string.hpp"
|
||||
|
||||
using namespace testing;
|
||||
using namespace std;
|
||||
|
||||
class PolicyTest : public Test
|
||||
{
|
||||
public:
|
||||
PolicyTest() {}
|
||||
|
||||
void
|
||||
orchestrationPolicyToString(stringstream &string_stream)
|
||||
{
|
||||
cereal::JSONInputArchive archive_in(string_stream);
|
||||
orchestration_policy.serialize(archive_in);
|
||||
}
|
||||
|
||||
OrchestrationPolicy orchestration_policy;
|
||||
};
|
||||
|
||||
TEST_F(PolicyTest, doNothing)
|
||||
{
|
||||
}
|
||||
|
||||
TEST_F(PolicyTest, serialization)
|
||||
{
|
||||
stringstream string_stream;
|
||||
string_stream << "{"
|
||||
" \"fog-address\": \"http://10.0.0.18:81/control/\","
|
||||
" \"agent-type\": \"13324sadsd2\","
|
||||
" \"pulling-interval\": 20,"
|
||||
" \"error-pulling-interval\": 15"
|
||||
"}";
|
||||
try {
|
||||
orchestrationPolicyToString(string_stream);
|
||||
} catch (cereal::Exception &e) {
|
||||
ASSERT_TRUE(false) << "Cereal threw an exception: " << e.what();
|
||||
}
|
||||
|
||||
EXPECT_EQ(15u, orchestration_policy.getErrorSleepInterval());
|
||||
EXPECT_EQ(20u, orchestration_policy.getSleepInterval());
|
||||
EXPECT_EQ("http://10.0.0.18:81/control/", orchestration_policy.getFogAddress());
|
||||
}
|
||||
|
||||
TEST_F(PolicyTest, noAgentType)
|
||||
{
|
||||
stringstream string_stream;
|
||||
string_stream << "{"
|
||||
" \"fog-address\": \"http://10.0.0.18:81/control/\","
|
||||
" \"agent-type\": \"\","
|
||||
" \"pulling-interval\": 20,"
|
||||
" \"error-pulling-interval\": 15"
|
||||
"}";
|
||||
try {
|
||||
orchestrationPolicyToString(string_stream);
|
||||
} catch (cereal::Exception &e) {
|
||||
ASSERT_TRUE(false) << "Cereal threw an exception: " << e.what();
|
||||
}
|
||||
|
||||
EXPECT_EQ(15u, orchestration_policy.getErrorSleepInterval());
|
||||
EXPECT_EQ(20u, orchestration_policy.getSleepInterval());
|
||||
EXPECT_EQ("http://10.0.0.18:81/control/", orchestration_policy.getFogAddress());
|
||||
}
|
||||
|
||||
TEST_F(PolicyTest, zeroSleepIntervels)
|
||||
{
|
||||
stringstream string_stream;
|
||||
string_stream << "{"
|
||||
" \"fog-address\": \"http://10.0.0.18:81/control/\","
|
||||
" \"agent-type\": \"13324sadsd2\","
|
||||
" \"pulling-interval\": 0,"
|
||||
" \"error-pulling-interval\": 0"
|
||||
"}";
|
||||
try {
|
||||
orchestrationPolicyToString(string_stream);
|
||||
} catch (cereal::Exception &e) {
|
||||
ASSERT_TRUE(false) << "Cereal threw an exception: " << e.what();
|
||||
}
|
||||
|
||||
EXPECT_EQ(0u, orchestration_policy.getErrorSleepInterval());
|
||||
EXPECT_EQ(0u, orchestration_policy.getSleepInterval());
|
||||
EXPECT_EQ("http://10.0.0.18:81/control/", orchestration_policy.getFogAddress());
|
||||
}
|
||||
|
||||
TEST_F(PolicyTest, operatorEqual)
|
||||
{
|
||||
stringstream string_stream;
|
||||
string_stream << "{"
|
||||
" \"fog-address\": \"http://10.0.0.18:81/control/\","
|
||||
" \"pulling-interval\": 20,"
|
||||
" \"error-pulling-interval\": 15"
|
||||
"}";
|
||||
try {
|
||||
orchestrationPolicyToString(string_stream);
|
||||
} catch (cereal::Exception &e) {
|
||||
ASSERT_TRUE(false) << "Cereal threw an exception: " << e.what();
|
||||
}
|
||||
|
||||
OrchestrationPolicy orchestration_copy_policy;
|
||||
stringstream string_stream_copy;
|
||||
string_stream_copy << "{"
|
||||
" \"fog-address\": \"http://10.0.0.18:81/control/\","
|
||||
" \"pulling-interval\": 20,"
|
||||
" \"error-pulling-interval\": 15"
|
||||
"}";
|
||||
try{
|
||||
cereal::JSONInputArchive archive_in(string_stream_copy);
|
||||
orchestration_copy_policy.serialize(archive_in);
|
||||
} catch (cereal::Exception &e) {
|
||||
ASSERT_TRUE(false) << "Cereal threw an exception: " << e.what();
|
||||
}
|
||||
EXPECT_TRUE(orchestration_copy_policy == orchestration_policy);
|
||||
EXPECT_FALSE(orchestration_copy_policy != orchestration_policy);
|
||||
|
||||
OrchestrationPolicy orchestration_new_policy;
|
||||
stringstream string_stream_new;
|
||||
string_stream_new << "{"
|
||||
" \"fog-address\": \"http://10.0.0.18:801/control/\","
|
||||
" \"pulling-interval\": 20,"
|
||||
" \"error-pulling-interval\": 15"
|
||||
"}";
|
||||
try{
|
||||
cereal::JSONInputArchive archive_in(string_stream_new);
|
||||
orchestration_new_policy.serialize(archive_in);
|
||||
} catch (cereal::Exception &e) {
|
||||
ASSERT_TRUE(false) << "Cereal threw an exception: " << e.what();
|
||||
}
|
||||
EXPECT_FALSE(orchestration_new_policy == orchestration_policy);
|
||||
EXPECT_TRUE(orchestration_new_policy != orchestration_policy);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(PolicyTest, newOptionalFields)
|
||||
{
|
||||
stringstream string_stream;
|
||||
string_stream << "{"
|
||||
" \"fog-address\": \"https://fog-api-gw-agents.cloud.ngen.checkpoint.com\","
|
||||
" \"pulling-interval\": 30,"
|
||||
" \"error-pulling-interval\": 10,"
|
||||
" \"agent-type\": \"arrow\""
|
||||
"}";
|
||||
|
||||
try {
|
||||
orchestrationPolicyToString(string_stream);
|
||||
} catch (cereal::Exception &e) {
|
||||
ASSERT_TRUE(false) << "Cereal threw an exception: " << e.what();
|
||||
}
|
||||
|
||||
EXPECT_EQ(10u, orchestration_policy.getErrorSleepInterval());
|
||||
EXPECT_EQ(30u, orchestration_policy.getSleepInterval());
|
||||
EXPECT_EQ("https://fog-api-gw-agents.cloud.ngen.checkpoint.com", orchestration_policy.getFogAddress());
|
||||
}
|
||||
@@ -0,0 +1,484 @@
|
||||
#include "orchestration_status.h"
|
||||
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
|
||||
#include "cptest.h"
|
||||
#include "config.h"
|
||||
#include "config_component.h"
|
||||
#include "mock/mock_time_get.h"
|
||||
#include "mock/mock_orchestration_tools.h"
|
||||
#include "mock/mock_agent_details.h"
|
||||
#include "mock/mock_mainloop.h"
|
||||
#include "mock/mock_rest_api.h"
|
||||
|
||||
using namespace testing;
|
||||
using namespace std;
|
||||
using namespace chrono;
|
||||
|
||||
class OrchestrationStatusTest : public Test
|
||||
{
|
||||
public:
|
||||
~OrchestrationStatusTest() { Debug::setNewDefaultStdout(&cout); }
|
||||
|
||||
void
|
||||
init()
|
||||
{
|
||||
Debug::setUnitTestFlag(D_ORCHESTRATOR, Debug::DebugLevel::TRACE);
|
||||
Debug::setNewDefaultStdout(&capture_debug);
|
||||
CPTestTempfile status_file;
|
||||
file_path = status_file.fname;
|
||||
setConfiguration(file_path, "orchestration", "Orchestration status path");
|
||||
// Write orchestration status to file routine
|
||||
EXPECT_CALL(
|
||||
mock_mainloop,
|
||||
addRecurringRoutine(I_MainLoop::RoutineType::Timer, chrono::microseconds(5000000), _, _, false))
|
||||
.WillOnce(DoAll(SaveArg<2>(&routine), Return(1))
|
||||
);
|
||||
EXPECT_CALL(mock_tools, readFile(file_path)).WillOnce(Return(start_file_content));
|
||||
orchestration_status.init();
|
||||
}
|
||||
|
||||
string
|
||||
orchestrationStatusFileToString()
|
||||
{
|
||||
routine();
|
||||
ifstream status_file(file_path);
|
||||
stringstream string_stream;
|
||||
if (status_file.is_open()) {
|
||||
string line;
|
||||
bool is_first_line = true;
|
||||
while (getline(status_file, line)) {
|
||||
if (is_first_line) {
|
||||
is_first_line = false;
|
||||
} else {
|
||||
string_stream << endl;
|
||||
}
|
||||
string_stream << line;
|
||||
}
|
||||
status_file.close();
|
||||
}
|
||||
return string_stream.str();
|
||||
}
|
||||
|
||||
string
|
||||
buildOrchestrationStatusJSON(
|
||||
const string &last_update_attempt = "None",
|
||||
const string &last_update_status = "None",
|
||||
const string &last_update = "None",
|
||||
const string &last_manifest_update = "None",
|
||||
const string &policy_version = "",
|
||||
const string &last_policy_update = "None",
|
||||
const string &last_settings_update = "None",
|
||||
const string &upgrade_mode = "None",
|
||||
const string &fog_address = "None",
|
||||
const string ®istration_status = "None",
|
||||
const string &manifest_status = "None",
|
||||
const string ®istration_details_name = "",
|
||||
const string ®istration_details_type = "",
|
||||
const string ®istration_details_platform = "",
|
||||
const string ®istration_details_architecture = "",
|
||||
const string &agent_id = "None",
|
||||
const string &profile_id = "None",
|
||||
const string &tenant_id = "None"
|
||||
)
|
||||
{
|
||||
return "{\n"
|
||||
" \"Last update attempt\": \"" + last_update_attempt + "\",\n"
|
||||
" \"Last update status\": \"" + last_update_status + "\",\n"
|
||||
" \"Last update\": \"" + last_update + "\",\n"
|
||||
" \"Last manifest update\": \"" + last_manifest_update + "\",\n"
|
||||
" \"Policy version\": \"" + policy_version + "\",\n"
|
||||
" \"Last policy update\": \"" + last_policy_update + "\",\n"
|
||||
" \"Last settings update\": \"" + last_settings_update + "\",\n"
|
||||
" \"Upgrade mode\": \"" + upgrade_mode + "\",\n"
|
||||
" \"Fog address\": \"" + fog_address + "\",\n"
|
||||
" \"Registration status\": \"" + registration_status + "\",\n"
|
||||
" \"Registration details\": {\n"
|
||||
" \"Name\": \"" + registration_details_name + "\",\n"
|
||||
" \"Type\": \"" + registration_details_type + "\",\n"
|
||||
" \"Platform\": \"" + registration_details_platform + "\",\n"
|
||||
" \"Architecture\": \"" + registration_details_architecture + "\"\n"
|
||||
" },\n"
|
||||
" \"Agent ID\": \"" + agent_id + "\",\n"
|
||||
" \"Profile ID\": \"" + profile_id + "\",\n"
|
||||
" \"Tenant ID\": \"" + tenant_id + "\",\n"
|
||||
" \"Manifest status\": \"" + manifest_status + "\",\n"
|
||||
" \"Service policy\": {},\n"
|
||||
" \"Service settings\": {}\n"
|
||||
"}";
|
||||
}
|
||||
|
||||
::Environment env;
|
||||
ConfigComponent config;
|
||||
StrictMock<MockTimeGet> time;
|
||||
StrictMock<MockMainLoop> mock_mainloop;
|
||||
ostringstream capture_debug;
|
||||
StrictMock<MockOrchestrationTools> mock_tools;
|
||||
StrictMock<MockAgentDetails> mock_agent_details;
|
||||
OrchestrationStatus orchestration_status;
|
||||
I_OrchestrationStatus * i_orchestration_status =
|
||||
Singleton::Consume<I_OrchestrationStatus>::from(orchestration_status);
|
||||
string file_path;
|
||||
Maybe<string> start_file_content = genError("No file");
|
||||
I_MainLoop::Routine routine;
|
||||
};
|
||||
|
||||
TEST_F(OrchestrationStatusTest, doNothing)
|
||||
{
|
||||
}
|
||||
|
||||
TEST_F(OrchestrationStatusTest, noFieldsValues)
|
||||
{
|
||||
init();
|
||||
auto result = orchestrationStatusFileToString();
|
||||
EXPECT_EQ(buildOrchestrationStatusJSON(), result);
|
||||
}
|
||||
|
||||
TEST_F(OrchestrationStatusTest, recoverFields)
|
||||
{
|
||||
init();
|
||||
auto result = orchestrationStatusFileToString();
|
||||
i_orchestration_status->recoverFields();
|
||||
EXPECT_EQ(orchestrationStatusFileToString(), result);
|
||||
}
|
||||
|
||||
TEST_F(OrchestrationStatusTest, loadFromFile)
|
||||
{
|
||||
Maybe<string> status = genError("No file");;
|
||||
CPTestTempfile status_file;
|
||||
file_path = status_file.fname;
|
||||
setConfiguration(file_path, "orchestration", "Orchestration status path");
|
||||
// Write to file routine
|
||||
EXPECT_CALL(
|
||||
mock_mainloop,
|
||||
addRecurringRoutine(I_MainLoop::RoutineType::Timer, chrono::microseconds(5000000), _, _, false)
|
||||
).Times(3).WillRepeatedly(DoAll(SaveArg<2>(&routine), Return(1)));
|
||||
|
||||
EXPECT_CALL(mock_tools, readFile(file_path)).Times(3).WillRepeatedly(Return(status));
|
||||
orchestration_status.init();
|
||||
status = orchestrationStatusFileToString();
|
||||
|
||||
orchestration_status.init();
|
||||
EXPECT_EQ(orchestrationStatusFileToString(), status.unpack());
|
||||
|
||||
EXPECT_CALL(time, getLocalTimeStr())
|
||||
.WillOnce(Return(string("attempt time")))
|
||||
.WillOnce(Return(string("current time")));
|
||||
i_orchestration_status->setLastUpdateAttempt();
|
||||
i_orchestration_status->setFieldStatus(
|
||||
OrchestrationStatusFieldType::LAST_UPDATE,
|
||||
OrchestrationStatusResult::SUCCESS
|
||||
);
|
||||
|
||||
status = orchestrationStatusFileToString();
|
||||
EXPECT_EQ(buildOrchestrationStatusJSON("attempt time", "Succeeded ", "current time"), status.unpack());
|
||||
|
||||
// Write status to file
|
||||
routine();
|
||||
|
||||
// Reload status from file and validate status
|
||||
orchestration_status.init();
|
||||
EXPECT_EQ(buildOrchestrationStatusJSON("attempt time", "Succeeded ", "current time"), status.unpack());
|
||||
}
|
||||
|
||||
TEST_F(OrchestrationStatusTest, checkUpdateStatus)
|
||||
{
|
||||
init();
|
||||
EXPECT_CALL(time, getLocalTimeStr())
|
||||
.WillOnce(Return(string("attempt time")))
|
||||
.WillOnce(Return(string("current time")));
|
||||
|
||||
i_orchestration_status->setLastUpdateAttempt();
|
||||
|
||||
i_orchestration_status->setFieldStatus(
|
||||
OrchestrationStatusFieldType::LAST_UPDATE,
|
||||
OrchestrationStatusResult::SUCCESS
|
||||
);
|
||||
auto result = orchestrationStatusFileToString();
|
||||
EXPECT_EQ(buildOrchestrationStatusJSON("attempt time", "Succeeded ", "current time"), result);
|
||||
}
|
||||
|
||||
TEST_F(OrchestrationStatusTest, recoveryFields)
|
||||
{
|
||||
init();
|
||||
CPTestTempfile status({""});
|
||||
setConfiguration(status.fname, "orchestration", "Orchestration status path");
|
||||
|
||||
i_orchestration_status->setFieldStatus(
|
||||
OrchestrationStatusFieldType::REGISTRATION,
|
||||
OrchestrationStatusResult::SUCCESS
|
||||
);
|
||||
const string agent_id = "AgentId";
|
||||
const string profile_id = "ProfileId";
|
||||
const string tenant_id = "TenantId";
|
||||
auto fog_addr = Maybe<string>(string("FogDomain"));
|
||||
|
||||
EXPECT_CALL(mock_agent_details, getAgentId()).WillOnce(Return(agent_id));
|
||||
EXPECT_CALL(mock_agent_details, getProfileId()).WillOnce(Return(profile_id));
|
||||
EXPECT_CALL(mock_agent_details, getTenantId()).WillOnce(Return(tenant_id));
|
||||
EXPECT_CALL(mock_agent_details, getFogDomain()).WillOnce(Return(fog_addr));
|
||||
i_orchestration_status->writeStatusToFile();
|
||||
EXPECT_THAT(capture_debug.str(), HasSubstr("Repairing status fields"));
|
||||
|
||||
EXPECT_EQ(i_orchestration_status->getAgentId(), agent_id);
|
||||
EXPECT_EQ(i_orchestration_status->getProfileId(), profile_id);
|
||||
EXPECT_EQ(i_orchestration_status->getTenantId(), tenant_id);
|
||||
EXPECT_EQ(i_orchestration_status->getFogAddress(), fog_addr.unpack());
|
||||
}
|
||||
|
||||
TEST_F(OrchestrationStatusTest, updateAllLastUpdatesTypes)
|
||||
{
|
||||
init();
|
||||
EXPECT_CALL(time, getLocalTimeStr())
|
||||
.WillOnce(Return(string("attempt time")))
|
||||
.WillOnce(Return(string("current time")))
|
||||
.WillOnce(Return(string("current time001")));
|
||||
|
||||
i_orchestration_status->setLastUpdateAttempt();
|
||||
|
||||
i_orchestration_status->setFieldStatus(
|
||||
OrchestrationStatusFieldType::LAST_UPDATE,
|
||||
OrchestrationStatusResult::SUCCESS
|
||||
);
|
||||
i_orchestration_status->setIsConfigurationUpdated(
|
||||
EnumArray<OrchestrationStatusConfigType, bool>(true, false, false)
|
||||
);
|
||||
auto result = orchestrationStatusFileToString();
|
||||
EXPECT_EQ(buildOrchestrationStatusJSON("attempt time", "Succeeded ", "current time", "current time001"), result);
|
||||
|
||||
EXPECT_CALL(time, getLocalTimeStr())
|
||||
.Times(2)
|
||||
.WillRepeatedly(Return(string("current time002")));
|
||||
|
||||
i_orchestration_status->setFieldStatus(
|
||||
OrchestrationStatusFieldType::LAST_UPDATE,
|
||||
OrchestrationStatusResult::SUCCESS
|
||||
);
|
||||
i_orchestration_status->setIsConfigurationUpdated(
|
||||
EnumArray<OrchestrationStatusConfigType, bool>(true, true, false)
|
||||
);
|
||||
result = orchestrationStatusFileToString();
|
||||
EXPECT_EQ(
|
||||
buildOrchestrationStatusJSON(
|
||||
"attempt time",
|
||||
"Succeeded ",
|
||||
"current time002",
|
||||
"current time002",
|
||||
"",
|
||||
"current time002"
|
||||
),
|
||||
result
|
||||
);
|
||||
|
||||
EXPECT_CALL(time, getLocalTimeStr())
|
||||
.Times(2)
|
||||
.WillRepeatedly(Return(string("current time003")));
|
||||
|
||||
i_orchestration_status->setFieldStatus(
|
||||
OrchestrationStatusFieldType::LAST_UPDATE,
|
||||
OrchestrationStatusResult::SUCCESS
|
||||
);
|
||||
i_orchestration_status->setIsConfigurationUpdated(
|
||||
EnumArray<OrchestrationStatusConfigType, bool>(true, true, true)
|
||||
);
|
||||
result = orchestrationStatusFileToString();
|
||||
EXPECT_EQ(
|
||||
buildOrchestrationStatusJSON(
|
||||
"attempt time",
|
||||
"Succeeded ",
|
||||
"current time003",
|
||||
"current time003",
|
||||
"",
|
||||
"current time003",
|
||||
"current time003"
|
||||
),
|
||||
result
|
||||
);
|
||||
}
|
||||
|
||||
TEST_F(OrchestrationStatusTest, errorInRegistrationAndMainfest)
|
||||
{
|
||||
init();
|
||||
string fog_address = "http://fog.address";
|
||||
string registar_error = "Fail to registar";
|
||||
string manifest_error = "Fail to achieve manifest";
|
||||
string last_update_error = "Fail to update";
|
||||
|
||||
EXPECT_CALL(time, getLocalTimeStr()).Times(3).WillRepeatedly(Return(string("Time")));
|
||||
|
||||
i_orchestration_status->setFieldStatus(
|
||||
OrchestrationStatusFieldType::LAST_UPDATE,
|
||||
OrchestrationStatusResult::SUCCESS
|
||||
);
|
||||
i_orchestration_status->setIsConfigurationUpdated(
|
||||
EnumArray<OrchestrationStatusConfigType, bool>(true, true, true)
|
||||
);
|
||||
i_orchestration_status->setFieldStatus(
|
||||
OrchestrationStatusFieldType::LAST_UPDATE,
|
||||
OrchestrationStatusResult::FAILED,
|
||||
last_update_error
|
||||
);
|
||||
i_orchestration_status->setIsConfigurationUpdated(
|
||||
EnumArray<OrchestrationStatusConfigType, bool>(false, false, false)
|
||||
);
|
||||
|
||||
i_orchestration_status->setUpgradeMode("Online upgrades");
|
||||
i_orchestration_status->setFogAddress(fog_address);
|
||||
|
||||
i_orchestration_status->setUpgradeMode("Online upgrades");
|
||||
i_orchestration_status->setFogAddress(fog_address);
|
||||
|
||||
i_orchestration_status->setFieldStatus(
|
||||
OrchestrationStatusFieldType::REGISTRATION,
|
||||
OrchestrationStatusResult::FAILED,
|
||||
registar_error
|
||||
);
|
||||
i_orchestration_status->setFieldStatus(
|
||||
OrchestrationStatusFieldType::MANIFEST,
|
||||
OrchestrationStatusResult::FAILED,
|
||||
manifest_error
|
||||
);
|
||||
EXPECT_EQ(i_orchestration_status->getManifestError(), manifest_error);
|
||||
|
||||
auto result = orchestrationStatusFileToString();
|
||||
EXPECT_EQ(
|
||||
buildOrchestrationStatusJSON(
|
||||
"None",
|
||||
"Failed. Reason: " + last_update_error,
|
||||
"Time",
|
||||
"Time",
|
||||
"",
|
||||
"Time",
|
||||
"Time",
|
||||
"Online upgrades",
|
||||
fog_address,
|
||||
"Failed. Reason: " + registar_error,
|
||||
"Failed. Reason: " + manifest_error
|
||||
),
|
||||
result
|
||||
);
|
||||
}
|
||||
|
||||
TEST_F(OrchestrationStatusTest, setAllFields)
|
||||
{
|
||||
init();
|
||||
string fog_address = "http://fog.address";
|
||||
EXPECT_CALL(time, getLocalTimeStr())
|
||||
.Times(3)
|
||||
.WillRepeatedly(Return(string("current time")));
|
||||
i_orchestration_status->setFieldStatus(
|
||||
OrchestrationStatusFieldType::LAST_UPDATE,
|
||||
OrchestrationStatusResult::SUCCESS
|
||||
);
|
||||
i_orchestration_status->setIsConfigurationUpdated(
|
||||
EnumArray<OrchestrationStatusConfigType, bool>(true, true, true)
|
||||
);
|
||||
i_orchestration_status->setRegistrationDetails("name", "type", "platform", "arch");
|
||||
i_orchestration_status->setAgentDetails("id", "profile", "tenant");
|
||||
i_orchestration_status->setFogAddress("http://fog.address");
|
||||
i_orchestration_status->setPolicyVersion("12");
|
||||
i_orchestration_status->setAgentType("test_type");
|
||||
i_orchestration_status->setUpgradeMode("Test Mode");
|
||||
i_orchestration_status->setRegistrationStatus("Succeeded");
|
||||
i_orchestration_status->setFieldStatus(
|
||||
OrchestrationStatusFieldType::REGISTRATION,
|
||||
OrchestrationStatusResult::SUCCESS
|
||||
);
|
||||
i_orchestration_status->setFieldStatus(
|
||||
OrchestrationStatusFieldType::MANIFEST,
|
||||
OrchestrationStatusResult::SUCCESS
|
||||
);
|
||||
|
||||
string non_empty_conf = "{x:y}";
|
||||
string curr_mock_path = "path";
|
||||
EXPECT_CALL(mock_tools, readFile(curr_mock_path)).WillRepeatedly(Return(non_empty_conf));
|
||||
EXPECT_CALL(mock_tools, readFile(string("new_path"))).WillOnce(Return(string("{}")));
|
||||
|
||||
i_orchestration_status->setServiceConfiguration(
|
||||
"service_a", "path", OrchestrationStatusConfigType::SETTINGS
|
||||
);
|
||||
i_orchestration_status->setServiceConfiguration(
|
||||
"service_b", "path", OrchestrationStatusConfigType::POLICY
|
||||
);
|
||||
i_orchestration_status->setServiceConfiguration(
|
||||
"service_c", "path", OrchestrationStatusConfigType::POLICY
|
||||
);
|
||||
i_orchestration_status->setServiceConfiguration(
|
||||
"service_c", "new_path", OrchestrationStatusConfigType::POLICY
|
||||
);
|
||||
i_orchestration_status->setLastUpdateAttempt();
|
||||
|
||||
auto result = orchestrationStatusFileToString();
|
||||
|
||||
string expected = "{\n"
|
||||
" \"Last update attempt\": \"current time\",\n"
|
||||
" \"Last update status\": \"Succeeded \",\n"
|
||||
" \"Last update\": \"current time\",\n"
|
||||
" \"Last manifest update\": \"current time\",\n"
|
||||
" \"Policy version\": \"12\",\n"
|
||||
" \"Last policy update\": \"current time\",\n"
|
||||
" \"Last settings update\": \"current time\",\n"
|
||||
" \"Upgrade mode\": \"Test Mode\",\n"
|
||||
" \"Fog address\": \"http://fog.address\",\n"
|
||||
" \"Registration status\": \"Succeeded \",\n"
|
||||
" \"Registration details\": {\n"
|
||||
" \"Name\": \"name\",\n"
|
||||
" \"Type\": \"test_type\",\n"
|
||||
" \"Platform\": \"platform\",\n"
|
||||
" \"Architecture\": \"arch\"\n"
|
||||
" },\n"
|
||||
" \"Agent ID\": \"id\",\n"
|
||||
" \"Profile ID\": \"profile\",\n"
|
||||
" \"Tenant ID\": \"tenant\",\n"
|
||||
" \"Manifest status\": \"Succeeded \",\n"
|
||||
" \"Service policy\": {\n"
|
||||
" \"service_b\": \"path\"\n"
|
||||
" },\n"
|
||||
" \"Service settings\": {\n"
|
||||
" \"service_a\": \"path\"\n"
|
||||
" }\n"
|
||||
"}";
|
||||
EXPECT_EQ(expected, result);
|
||||
|
||||
// Now lets check load from file
|
||||
routine();
|
||||
EXPECT_EQ(expected, orchestrationStatusFileToString());
|
||||
|
||||
EXPECT_CALL(
|
||||
mock_mainloop,
|
||||
addRecurringRoutine(I_MainLoop::RoutineType::Timer, chrono::microseconds(5000000), _, _, false))
|
||||
.WillOnce(DoAll(SaveArg<2>(&routine), Return(1)));
|
||||
EXPECT_CALL(mock_tools, readFile(file_path)).Times(1).WillOnce(Return(expected));
|
||||
orchestration_status.init();
|
||||
EXPECT_EQ(expected, orchestrationStatusFileToString());
|
||||
|
||||
map<string, string> service_map_a = {{"service_a", "path"}};
|
||||
map<string, string> service_map_b = {{"service_b", "path"}};
|
||||
|
||||
string agent_details =
|
||||
"\n Name: name"
|
||||
"\n Type: test_type"
|
||||
"\n Platform: platform"
|
||||
"\n Architecture: arch";
|
||||
|
||||
EXPECT_EQ(i_orchestration_status->getLastUpdateAttempt(), "current time");
|
||||
EXPECT_EQ(i_orchestration_status->getUpdateStatus(), "Succeeded ");;
|
||||
EXPECT_EQ(i_orchestration_status->getUpdateTime(), "current time");
|
||||
EXPECT_EQ(i_orchestration_status->getLastManifestUpdate(), "current time");
|
||||
EXPECT_EQ(i_orchestration_status->getPolicyVersion(), "12");
|
||||
EXPECT_EQ(i_orchestration_status->getLastPolicyUpdate(), "current time");
|
||||
EXPECT_EQ(i_orchestration_status->getLastSettingsUpdate(), "current time");
|
||||
EXPECT_EQ(i_orchestration_status->getUpgradeMode(), "Test Mode");
|
||||
EXPECT_EQ(i_orchestration_status->getFogAddress(), "http://fog.address");
|
||||
EXPECT_EQ(i_orchestration_status->getRegistrationStatus(), "Succeeded ");
|
||||
EXPECT_EQ(i_orchestration_status->getAgentId(), "id");
|
||||
EXPECT_EQ(i_orchestration_status->getProfileId(), "profile");
|
||||
EXPECT_EQ(i_orchestration_status->getTenantId(), "tenant");
|
||||
EXPECT_EQ(i_orchestration_status->getManifestStatus(), "Succeeded ");
|
||||
EXPECT_EQ(i_orchestration_status->getServicePolicies(), service_map_b);
|
||||
EXPECT_EQ(i_orchestration_status->getServiceSettings(), service_map_a);
|
||||
EXPECT_EQ(i_orchestration_status->getRegistrationDetails(), agent_details);
|
||||
}
|
||||
236
components/security_apps/orchestration/modules/modules_ut/package_ut.cc
Executable file
236
components/security_apps/orchestration/modules/modules_ut/package_ut.cc
Executable file
@@ -0,0 +1,236 @@
|
||||
#include "package.h"
|
||||
|
||||
#include "cptest.h"
|
||||
#include "cereal/types/string.hpp"
|
||||
#include "cereal/archives/json.hpp"
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <fstream>
|
||||
|
||||
using namespace testing;
|
||||
using namespace std;
|
||||
|
||||
class PackageTest : public Test
|
||||
{
|
||||
public:
|
||||
PackageTest() {}
|
||||
|
||||
bool
|
||||
load(stringstream &string_stream, Package &package)
|
||||
{
|
||||
try {
|
||||
cereal::JSONInputArchive archive_in(string_stream);
|
||||
package.serialize(archive_in);
|
||||
} catch (const cereal::Exception &) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
write(const string &path, Package &package)
|
||||
{
|
||||
std::ofstream os(path);
|
||||
cereal::JSONOutputArchive archive_out(os);
|
||||
package.serialize(archive_out);
|
||||
}
|
||||
|
||||
string
|
||||
readFile(const string &path)
|
||||
{
|
||||
ifstream text_file(path);
|
||||
stringstream buffer;
|
||||
buffer << text_file.rdbuf();
|
||||
return buffer.str();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(PackageTest, doNothing)
|
||||
{
|
||||
}
|
||||
|
||||
TEST_F(PackageTest, serializationFromString)
|
||||
{
|
||||
stringstream string_stream;
|
||||
string_stream << "{"
|
||||
" \"version\": \"c\","
|
||||
" \"download-path\": \"https://a/install_orchestration.sh\",\n"
|
||||
" \"relative-path\": \"/install_orchestration.sh\",\n"
|
||||
" \"name\": \"orchestration\","
|
||||
" \"checksum-type\": \"sha1sum\","
|
||||
" \"checksum\": \"8d4a5709673a05b380ba7d6567e28910019118f5\","
|
||||
" \"package-type\": \"service\","
|
||||
" \"require\": []"
|
||||
"}";
|
||||
Package package;
|
||||
EXPECT_EQ(true, load(string_stream, package));
|
||||
|
||||
vector<string> links = { "https://10.0.0.18/install_orchestration.sh", "ftp://172.23.92.135/policy.txt" };
|
||||
|
||||
EXPECT_EQ("orchestration", package.getName());
|
||||
EXPECT_EQ(Package::ChecksumTypes::SHA1, package.getChecksumType());
|
||||
EXPECT_EQ("8d4a5709673a05b380ba7d6567e28910019118f5", package.getChecksum());
|
||||
EXPECT_EQ("orchestration", package.getName());
|
||||
EXPECT_EQ("c", package.getVersion());
|
||||
EXPECT_EQ(Package::PackageType::Service, package.getType());
|
||||
EXPECT_TRUE(package.isInstallable().ok());
|
||||
}
|
||||
|
||||
TEST_F(PackageTest, writeAsJson)
|
||||
{
|
||||
stringstream string_stream;
|
||||
string_stream << "{\n"
|
||||
" \"download-path\": \"https://a/install_orchestration.sh\",\n"
|
||||
" \"relative-path\": \"/install_orchestration.sh\",\n"
|
||||
" \"version\": \"c\",\n"
|
||||
" \"name\": \"orchestration\",\n"
|
||||
" \"checksum-type\": \"sha1sum\",\n"
|
||||
" \"checksum\": \"8d4a5709673a05b380ba7d6567e28910019118f5\",\n"
|
||||
" \"package-type\": \"service\"\n"
|
||||
"}";
|
||||
Package package;
|
||||
EXPECT_EQ(true, load(string_stream, package));
|
||||
|
||||
vector<string> links = { "https://10.0.0.18/install_orchestration.sh", "ftp://172.23.92.135/policy.txt" };
|
||||
|
||||
EXPECT_EQ("orchestration", package.getName());
|
||||
EXPECT_EQ(Package::ChecksumTypes::SHA1, package.getChecksumType());
|
||||
EXPECT_EQ("8d4a5709673a05b380ba7d6567e28910019118f5", package.getChecksum());
|
||||
EXPECT_EQ("orchestration", package.getName());
|
||||
EXPECT_EQ("c", package.getVersion());
|
||||
EXPECT_EQ(Package::PackageType::Service, package.getType());
|
||||
EXPECT_TRUE(package.isInstallable().ok());
|
||||
|
||||
write("service.json", package);
|
||||
string data = readFile("service.json");
|
||||
EXPECT_EQ(string_stream.str(), data);
|
||||
}
|
||||
|
||||
TEST_F(PackageTest, eqService)
|
||||
{
|
||||
stringstream string_stream;
|
||||
string_stream << "{\n"
|
||||
" \"download-path\": \"https://a/install_orchestration.sh\",\n"
|
||||
" \"relative-path\": \"/install_orchestration.sh\",\n"
|
||||
" \"version\": \"c\",\n"
|
||||
" \"name\": \"orchestration\",\n"
|
||||
" \"checksum-type\": \"sha1sum\",\n"
|
||||
" \"checksum\": \"8d4a5709673a05b380ba7d6567e28910019118f5\",\n"
|
||||
" \"package-type\": \"service\"\n"
|
||||
"}";
|
||||
Package package;
|
||||
Package package2;
|
||||
EXPECT_TRUE(load(string_stream, package));
|
||||
string_stream.clear();
|
||||
string_stream << "{\n"
|
||||
" \"download-path\": \"https://a/install_orchestration.sh\",\n"
|
||||
" \"relative-path\": \"/install_orchestration.sh\",\n"
|
||||
" \"version\": \"c\",\n"
|
||||
" \"name\": \"orchestration\",\n"
|
||||
" \"checksum-type\": \"sha1sum\",\n"
|
||||
" \"checksum\": \"8d4a5709673a05b380ba7d6567e28910000000000\",\n"
|
||||
" \"package-type\": \"service\"\n"
|
||||
"}";
|
||||
EXPECT_TRUE(load(string_stream, package));
|
||||
EXPECT_TRUE(package != package2);
|
||||
}
|
||||
|
||||
TEST_F(PackageTest, changeDir)
|
||||
{
|
||||
stringstream string_stream;
|
||||
string_stream << "{\n"
|
||||
" \"download-path\": \"https://a/install_orchestration.sh\",\n"
|
||||
" \"relative-path\": \"/install_orchestration.sh\",\n"
|
||||
" \"version\": \"c\",\n"
|
||||
" \"name\": \"../..\",\n"
|
||||
" \"checksum-type\": \"sha1sum\",\n"
|
||||
" \"checksum\": \"8d4a5709673a05b380ba7d6567e28910019118f5\",\n"
|
||||
" \"package-type\": \"service\"\n"
|
||||
"}";
|
||||
Package package;
|
||||
EXPECT_FALSE(load(string_stream, package));
|
||||
}
|
||||
|
||||
TEST_F(PackageTest, mkdirCommand)
|
||||
{
|
||||
stringstream string_stream;
|
||||
string_stream << "{\n"
|
||||
" \"download-path\": \"https://a/install_orchestration.sh\",\n"
|
||||
" \"relative-path\": \"/install_orchestration.sh\",\n"
|
||||
" \"version\": \"c\",\n"
|
||||
" \"name\": \"mkdir ../../something\",\n"
|
||||
" \"checksum-type\": \"sha1sum\",\n"
|
||||
" \"checksum\": \"8d4a5709673a05b380ba7d6567e28910019118f5\",\n"
|
||||
" \"package-type\": \"service\"\n"
|
||||
"}";
|
||||
Package package;
|
||||
EXPECT_FALSE(load(string_stream, package));
|
||||
}
|
||||
|
||||
TEST_F(PackageTest, badPackageName)
|
||||
{
|
||||
stringstream string_stream;
|
||||
string_stream << "{\n"
|
||||
" \"download-path\": \"https://a/install_orchestration.sh\",\n"
|
||||
" \"relative-path\": \"/install_orchestration.sh\",\n"
|
||||
" \"version\": \"c\",\n"
|
||||
" \"name\": \"- - - - - -\",\n"
|
||||
" \"checksum-type\": \"sha1sum\",\n"
|
||||
" \"checksum\": \"8d4a5709673a05b380ba7d6567e28910019118f5\",\n"
|
||||
" \"package-type\": \"service\"\n"
|
||||
"}";
|
||||
Package package;
|
||||
EXPECT_FALSE(load(string_stream, package));
|
||||
}
|
||||
|
||||
TEST_F(PackageTest, anyOrder)
|
||||
{
|
||||
stringstream string_stream;
|
||||
string_stream << "{\n"
|
||||
" \"name\": \"asdQwe\",\n"
|
||||
" \"relative-path\": \"/install_orchestration.sh\",\n"
|
||||
" \"version\": \"c\",\n"
|
||||
" \"download-path\": \"https://a/install_orchestration.sh\",\n"
|
||||
" \"checksum\": \"8d4a5709673a05b380ba7d6567e28910019118f5\",\n"
|
||||
" \"package-type\": \"service\",\n"
|
||||
" \"checksum-type\": \"sha1sum\"\n"
|
||||
"}";
|
||||
Package package;
|
||||
EXPECT_TRUE(load(string_stream, package));
|
||||
}
|
||||
|
||||
TEST_F(PackageTest, anyOrderWithRequire)
|
||||
{
|
||||
stringstream string_stream;
|
||||
string_stream << "{\n"
|
||||
" \"require\": [],\n"
|
||||
" \"name\": \"asdQwe\",\n"
|
||||
" \"version\": \"c\",\n"
|
||||
" \"relative-path\": \"/install_orchestration.sh\",\n"
|
||||
" \"download-path\": \"https://a/install_orchestration.sh\",\n"
|
||||
" \"checksum\": \"8d4a5709673a05b380ba7d6567e28910019118f5\",\n"
|
||||
" \"package-type\": \"service\",\n"
|
||||
" \"checksum-type\": \"sha1sum\"\n"
|
||||
"}";
|
||||
Package package;
|
||||
EXPECT_TRUE(load(string_stream, package));
|
||||
}
|
||||
|
||||
TEST_F(PackageTest, uninstallablePackage)
|
||||
{
|
||||
stringstream string_stream;
|
||||
string_stream << "{\n"
|
||||
" \"name\": \"waap\",\n"
|
||||
" \"version\": \"\",\n"
|
||||
" \"download-path\": \"\",\n"
|
||||
" \"relative-path\": \"\",\n"
|
||||
" \"checksum\": \"\",\n"
|
||||
" \"package-type\": \"service\",\n"
|
||||
" \"checksum-type\": \"sha1sum\",\n"
|
||||
" \"status\": false,\n"
|
||||
" \"message\": \"This security app isn't valid for this agent\"\n"
|
||||
"}";
|
||||
Package package;
|
||||
EXPECT_TRUE(load(string_stream, package));
|
||||
EXPECT_THAT(package.isInstallable(), IsError("This security app isn't valid for this agent"));
|
||||
}
|
||||
117
components/security_apps/orchestration/modules/modules_ut/url_parser_ut.cc
Executable file
117
components/security_apps/orchestration/modules/modules_ut/url_parser_ut.cc
Executable file
@@ -0,0 +1,117 @@
|
||||
#include "url_parser.h"
|
||||
|
||||
#include "cptest.h"
|
||||
#include "mock/mock_orchestration_tools.h"
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
using namespace testing;
|
||||
using namespace std;
|
||||
|
||||
class URLParserTest : public Test
|
||||
{
|
||||
public:
|
||||
URLParserTest() {}
|
||||
|
||||
StrictMock<MockOrchestrationTools> mock_orchestration_tools;
|
||||
};
|
||||
|
||||
TEST_F(URLParserTest, doNothing)
|
||||
{
|
||||
}
|
||||
|
||||
TEST_F(URLParserTest, parseHTTP)
|
||||
{
|
||||
URLParser link("http://172.23.92.180:180/something");
|
||||
|
||||
EXPECT_FALSE(link.isOverSSL());
|
||||
EXPECT_EQ("180", link.getPort());
|
||||
EXPECT_EQ("/something", link.getQuery());
|
||||
}
|
||||
|
||||
TEST_F(URLParserTest, parseHTTPS)
|
||||
{
|
||||
URLParser link("https://172.23.92.180:180/something");
|
||||
|
||||
EXPECT_TRUE(link.isOverSSL());
|
||||
EXPECT_EQ("180", link.getPort());
|
||||
EXPECT_EQ("/something", link.getQuery());
|
||||
}
|
||||
|
||||
TEST_F(URLParserTest, parseAWS)
|
||||
{
|
||||
URLParser link("https://a58efa94efdf711e8a6540620a59b447-1878332922.eu-west-1.elb.amazonaws.com/");
|
||||
|
||||
EXPECT_TRUE(link.isOverSSL());
|
||||
EXPECT_EQ("443", link.getPort());
|
||||
EXPECT_EQ("a58efa94efdf711e8a6540620a59b447-1878332922.eu-west-1.elb.amazonaws.com", link.getBaseURL().unpack());
|
||||
EXPECT_EQ("", link.getQuery());
|
||||
}
|
||||
|
||||
TEST_F(URLParserTest, parseAWSWithoutSlash)
|
||||
{
|
||||
URLParser link("https://a58efa94efdf711e8a6540620a59b447-1878332922.eu-west-1.elb.amazonaws.com");
|
||||
|
||||
EXPECT_TRUE(link.isOverSSL());
|
||||
EXPECT_EQ("443", link.getPort());
|
||||
EXPECT_EQ("a58efa94efdf711e8a6540620a59b447-1878332922.eu-west-1.elb.amazonaws.com", link.getBaseURL().unpack());
|
||||
EXPECT_EQ("", link.getQuery());
|
||||
}
|
||||
|
||||
TEST_F(URLParserTest, protocolIsMissing)
|
||||
{
|
||||
// HTTPS is set by default when protocol is not present in URL.
|
||||
URLParser link("a58efa94efdf711e8a6540620a59b447-1878332922.eu-west-1.elb.amazonaws.com");
|
||||
|
||||
EXPECT_EQ(link.getBaseURL().unpack(), "a58efa94efdf711e8a6540620a59b447-1878332922.eu-west-1.elb.amazonaws.com");
|
||||
EXPECT_TRUE(link.isOverSSL());
|
||||
EXPECT_EQ("443", link.getPort());
|
||||
EXPECT_EQ("", link.getQuery());
|
||||
}
|
||||
|
||||
TEST_F(URLParserTest, parseBadURL)
|
||||
{
|
||||
URLParser link("http://this_is_not_https_site.com/something");
|
||||
|
||||
EXPECT_FALSE(link.isOverSSL());
|
||||
EXPECT_EQ("80", link.getPort());
|
||||
EXPECT_EQ("this_is_not_https_site.com", link.getBaseURL().unpack());
|
||||
EXPECT_EQ("/something", link.getQuery());
|
||||
}
|
||||
|
||||
TEST_F(URLParserTest, parseNothing)
|
||||
{
|
||||
URLParser link("");
|
||||
EXPECT_FALSE(link.getBaseURL().ok());
|
||||
EXPECT_TRUE(link.isOverSSL());
|
||||
EXPECT_EQ("443", link.getPort());
|
||||
EXPECT_EQ("", link.getQuery());
|
||||
}
|
||||
|
||||
TEST_F(URLParserTest, copyCtr)
|
||||
{
|
||||
URLParser link("");
|
||||
URLParser copy_link = link;
|
||||
EXPECT_TRUE(copy_link.isOverSSL());
|
||||
EXPECT_EQ("443", copy_link.getPort());
|
||||
EXPECT_EQ("", copy_link.getQuery());
|
||||
}
|
||||
|
||||
TEST_F(URLParserTest, printTest)
|
||||
{
|
||||
string url_path = "this_is_test_url";
|
||||
URLParser link(url_path);
|
||||
EXPECT_EQ("https://" + url_path + ":443", link.toString());
|
||||
stringstream ss;
|
||||
ss << link;
|
||||
EXPECT_EQ("https://" + url_path + ":443", ss.str());
|
||||
}
|
||||
TEST_F(URLParserTest, setQuery)
|
||||
{
|
||||
string url_path = "this_is_test_url/test.sh";
|
||||
URLParser link(url_path);
|
||||
EXPECT_EQ("https://" + url_path + ":443", link.toString());
|
||||
link.setQuery("/new-query");
|
||||
EXPECT_EQ("https://this_is_test_url/new-query:443", link.toString());
|
||||
}
|
||||
64
components/security_apps/orchestration/modules/orchestration_policy.cc
Executable file
64
components/security_apps/orchestration/modules/orchestration_policy.cc
Executable file
@@ -0,0 +1,64 @@
|
||||
// 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 "orchestration_policy.h"
|
||||
|
||||
#include "sasal.h"
|
||||
|
||||
SASAL_START // Orchestration - Modules
|
||||
|
||||
using namespace std;
|
||||
using namespace cereal;
|
||||
|
||||
const string &
|
||||
OrchestrationPolicy::getFogAddress() const
|
||||
{
|
||||
return fog_address;
|
||||
}
|
||||
|
||||
const unsigned long &
|
||||
OrchestrationPolicy::getSleepInterval() const
|
||||
{
|
||||
return sleep_interval;
|
||||
}
|
||||
|
||||
const unsigned long &
|
||||
OrchestrationPolicy::getErrorSleepInterval() const
|
||||
{
|
||||
return error_sleep_interval;
|
||||
}
|
||||
|
||||
void
|
||||
OrchestrationPolicy::serialize(JSONInputArchive &archive)
|
||||
{
|
||||
// Split it, so the order doesn't matter.
|
||||
archive(make_nvp("fog-address", fog_address));
|
||||
archive(make_nvp("pulling-interval", sleep_interval));
|
||||
archive(make_nvp("error-pulling-interval", error_sleep_interval));
|
||||
}
|
||||
|
||||
bool
|
||||
OrchestrationPolicy::operator==(const OrchestrationPolicy &other) const
|
||||
{
|
||||
return error_sleep_interval == other.error_sleep_interval &&
|
||||
sleep_interval == other.sleep_interval &&
|
||||
fog_address == other.fog_address;
|
||||
}
|
||||
|
||||
bool
|
||||
OrchestrationPolicy::operator!=(const OrchestrationPolicy &other) const
|
||||
{
|
||||
return !((*this) == other);
|
||||
}
|
||||
|
||||
SASAL_END
|
||||
685
components/security_apps/orchestration/modules/orchestration_status.cc
Executable file
685
components/security_apps/orchestration/modules/orchestration_status.cc
Executable file
@@ -0,0 +1,685 @@
|
||||
// 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 "orchestration_status.h"
|
||||
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
#include <algorithm>
|
||||
|
||||
#include "debug.h"
|
||||
#include "config.h"
|
||||
#include "sasal.h"
|
||||
|
||||
using namespace cereal;
|
||||
using namespace std;
|
||||
using namespace chrono;
|
||||
|
||||
USE_DEBUG_FLAG(D_ORCHESTRATOR);
|
||||
|
||||
SASAL_START // Orchestration - Modules
|
||||
|
||||
class RegistrationDetails
|
||||
{
|
||||
public:
|
||||
RegistrationDetails() = default;
|
||||
RegistrationDetails(const RegistrationDetails &) = default;
|
||||
RegistrationDetails(RegistrationDetails &&) = default;
|
||||
RegistrationDetails(
|
||||
string name,
|
||||
string type,
|
||||
string platform,
|
||||
string architecture)
|
||||
:
|
||||
name(name),
|
||||
type(type),
|
||||
platform(platform),
|
||||
architecture(architecture)
|
||||
{}
|
||||
|
||||
void
|
||||
serialize(cereal::JSONOutputArchive &archive)
|
||||
{
|
||||
if (type == "InfinityNextGateway") {
|
||||
type = "AppSecGateway";
|
||||
}
|
||||
archive(
|
||||
cereal::make_nvp("Name", name),
|
||||
cereal::make_nvp("Type", type),
|
||||
cereal::make_nvp("Platform", platform),
|
||||
cereal::make_nvp("Architecture", architecture)
|
||||
);
|
||||
}
|
||||
|
||||
void
|
||||
serialize(cereal::JSONInputArchive &archive)
|
||||
{
|
||||
archive(
|
||||
cereal::make_nvp("Name", name),
|
||||
cereal::make_nvp("Type", type),
|
||||
cereal::make_nvp("Platform", platform),
|
||||
cereal::make_nvp("Architecture", architecture)
|
||||
);
|
||||
if (type == "InfinityNextGateway") {
|
||||
type = "AppSecGateway";
|
||||
}
|
||||
}
|
||||
|
||||
RegistrationDetails & operator=(const RegistrationDetails &) = default;
|
||||
RegistrationDetails & operator=(RegistrationDetails &&) = default;
|
||||
void setAgentType(const string &_type) { type = _type; }
|
||||
|
||||
string
|
||||
toString() const
|
||||
{
|
||||
return
|
||||
"\n Name: " + name +
|
||||
"\n Type: " + type +
|
||||
"\n Platform: " + platform +
|
||||
"\n Architecture: " + architecture;
|
||||
}
|
||||
|
||||
private:
|
||||
string name;
|
||||
string type;
|
||||
string platform;
|
||||
string architecture;
|
||||
};
|
||||
|
||||
class Status
|
||||
{
|
||||
public:
|
||||
Status() = default;
|
||||
Status(const Status &) = default;
|
||||
Status(Status &&) = default;
|
||||
|
||||
Status & operator=(Status &&from) = default;
|
||||
Status & operator=(const Status &from)
|
||||
{
|
||||
last_update_status = from.last_update_status;
|
||||
last_update_time = from.last_update_time;
|
||||
last_update_attempt = from.last_update_attempt;
|
||||
last_manifest_update = from.last_manifest_update;
|
||||
policy_version = from.policy_version;
|
||||
last_policy_update = from.last_policy_update;
|
||||
last_settings_update = from.last_settings_update;
|
||||
upgrade_mode = from.upgrade_mode;
|
||||
fog_address = from.fog_address;
|
||||
registration_status = from.registration_status;
|
||||
manifest_status = from.manifest_status;
|
||||
agent_id = from.agent_id;
|
||||
profile_id = from.profile_id;
|
||||
tenant_id = from.tenant_id;
|
||||
registration_details = from.registration_details;
|
||||
service_policies = from.service_policies;
|
||||
service_settings = from.service_settings;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const string & getLastUpdateAttempt() const { return last_update_attempt; }
|
||||
const string & getUpdateStatus() const { return last_update_status; }
|
||||
const string & getUpdateTime() const { return last_update_time; }
|
||||
const string & getLastManifestUpdate() const { return last_manifest_update; }
|
||||
const string & getPolicyVersion() const { return policy_version; }
|
||||
const string & getLastPolicyUpdate() const { return last_policy_update; }
|
||||
const string & getLastSettingsUpdate() const { return last_settings_update; }
|
||||
const string & getUpgradeMode() const { return upgrade_mode; }
|
||||
const string & getFogAddress() const { return fog_address; }
|
||||
const string & getRegistrationStatus() const { return registration_status; }
|
||||
const string & getAgentId() const { return agent_id; }
|
||||
const string & getProfileId() const { return profile_id; }
|
||||
const string & getTenantId() const { return tenant_id; }
|
||||
const string & getManifestStatus() const { return manifest_status; }
|
||||
const string & getManifestError() const { return manifest_error; }
|
||||
const RegistrationDetails & getRegistrationDetails() const { return registration_details; }
|
||||
const map<string, string> & getServicePolicies() const { return service_policies; }
|
||||
const map<string, string> & getServiceSettings() const { return service_settings; }
|
||||
|
||||
void
|
||||
insertServicePolicy(const string &key, const string &value)
|
||||
{
|
||||
service_policies.insert(make_pair(key, value));
|
||||
}
|
||||
|
||||
void
|
||||
eraseServicePolicy(const string &key)
|
||||
{
|
||||
service_policies.erase(key);
|
||||
}
|
||||
|
||||
void
|
||||
insertServiceSetting(const string &key, const string &value)
|
||||
{
|
||||
service_settings.insert(make_pair(key, value));
|
||||
}
|
||||
|
||||
void
|
||||
eraseServiceSetting(const string &key)
|
||||
{
|
||||
service_settings.erase(key);
|
||||
}
|
||||
|
||||
void
|
||||
setIsConfigurationUpdated(
|
||||
EnumArray<OrchestrationStatusConfigType, bool> config_types,
|
||||
const string ¤t_time
|
||||
)
|
||||
{
|
||||
if (config_types[OrchestrationStatusConfigType::MANIFEST]) last_manifest_update = current_time;
|
||||
if (config_types[OrchestrationStatusConfigType::POLICY]) last_policy_update = current_time;
|
||||
if (config_types[OrchestrationStatusConfigType::SETTINGS]) last_settings_update = current_time;
|
||||
}
|
||||
|
||||
void
|
||||
setPolicyVersion(const string &_policy_version)
|
||||
{
|
||||
policy_version = _policy_version;
|
||||
}
|
||||
|
||||
void
|
||||
setRegistrationStatus(const string &_reg_status)
|
||||
{
|
||||
registration_status = _reg_status;
|
||||
}
|
||||
|
||||
void
|
||||
setUpgradeMode(const string &_upgrade_mode)
|
||||
{
|
||||
upgrade_mode = _upgrade_mode;
|
||||
}
|
||||
|
||||
void
|
||||
setAgentType(const string &_agent_type)
|
||||
{
|
||||
registration_details.setAgentType(_agent_type);
|
||||
}
|
||||
|
||||
void
|
||||
setAgentDetails(
|
||||
const string &_agent_id,
|
||||
const string &_profile_id,
|
||||
const string &_tenant_id)
|
||||
{
|
||||
agent_id = _agent_id;
|
||||
profile_id = _profile_id;
|
||||
tenant_id = _tenant_id;
|
||||
}
|
||||
|
||||
void
|
||||
setLastUpdateAttempt(const string &_last_update_attempt)
|
||||
{
|
||||
last_update_attempt = _last_update_attempt;
|
||||
}
|
||||
|
||||
void
|
||||
setFogAddress(const string &_fog_address)
|
||||
{
|
||||
fog_address = _fog_address;
|
||||
}
|
||||
|
||||
void
|
||||
setRegistrationDetails(
|
||||
const string &name,
|
||||
const string &type,
|
||||
const string &platform,
|
||||
const string &arch)
|
||||
{
|
||||
registration_details = RegistrationDetails(name, type, platform, arch);
|
||||
}
|
||||
|
||||
void
|
||||
setManifestStatus(const string &_manifest_status)
|
||||
{
|
||||
manifest_status = _manifest_status;
|
||||
}
|
||||
|
||||
void
|
||||
setManifestError(const string &error)
|
||||
{
|
||||
manifest_error = error;
|
||||
}
|
||||
|
||||
void
|
||||
setLastUpdateTime(const string &_last_update_time)
|
||||
{
|
||||
last_update_time = _last_update_time;
|
||||
}
|
||||
|
||||
void
|
||||
setLastUpdateStatus(const string &_last_update_status)
|
||||
{
|
||||
last_update_status = _last_update_status;
|
||||
}
|
||||
|
||||
void
|
||||
initValues()
|
||||
{
|
||||
last_update_attempt = "None";
|
||||
last_update_time = "None";
|
||||
last_update_status = "None";
|
||||
last_manifest_update = "None";
|
||||
last_policy_update = "None";
|
||||
last_settings_update = "None";
|
||||
fog_address = "None";
|
||||
agent_id = "None";
|
||||
profile_id = "None";
|
||||
tenant_id = "None";
|
||||
registration_status = "None";
|
||||
manifest_status = "None";
|
||||
upgrade_mode = "None";
|
||||
}
|
||||
|
||||
void
|
||||
recoverFields()
|
||||
{
|
||||
auto success_status = "Succeeded";
|
||||
if (fog_address == "None" && registration_status.find(success_status) != string::npos) {
|
||||
auto agent_details = Singleton::Consume<I_AgentDetails>::by<OrchestrationStatus>();
|
||||
dbgWarning(D_ORCHESTRATOR) << "Repairing status fields";
|
||||
agent_id = agent_details->getAgentId();
|
||||
profile_id = agent_details->getProfileId();
|
||||
tenant_id = agent_details->getTenantId();
|
||||
auto maybe_fog_domain = agent_details->getFogDomain();
|
||||
if (maybe_fog_domain.ok()) {
|
||||
fog_address = maybe_fog_domain.unpack();
|
||||
} else {
|
||||
fog_address = "None";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
serialize(cereal::JSONOutputArchive &archive)
|
||||
{
|
||||
recoverFields();
|
||||
archive(cereal::make_nvp("Last update attempt", last_update_attempt));
|
||||
archive(cereal::make_nvp("Last update status", last_update_status));
|
||||
archive(cereal::make_nvp("Last update", last_update_time));
|
||||
archive(cereal::make_nvp("Last manifest update", last_manifest_update));
|
||||
archive(cereal::make_nvp("Policy version", policy_version));
|
||||
archive(cereal::make_nvp("Last policy update", last_policy_update));
|
||||
archive(cereal::make_nvp("Last settings update", last_settings_update));
|
||||
archive(cereal::make_nvp("Upgrade mode", upgrade_mode));
|
||||
archive(cereal::make_nvp("Fog address", fog_address));
|
||||
archive(cereal::make_nvp("Registration status", registration_status));
|
||||
archive(cereal::make_nvp("Registration details", registration_details));
|
||||
archive(cereal::make_nvp("Agent ID", agent_id));
|
||||
archive(cereal::make_nvp("Profile ID", profile_id));
|
||||
archive(cereal::make_nvp("Tenant ID", tenant_id));
|
||||
archive(cereal::make_nvp("Manifest status", manifest_status));
|
||||
archive(cereal::make_nvp("Service policy", service_policies));
|
||||
archive(cereal::make_nvp("Service settings", service_settings));
|
||||
}
|
||||
|
||||
void
|
||||
serialize(cereal::JSONInputArchive &archive)
|
||||
{
|
||||
archive(cereal::make_nvp("Last update attempt", last_update_attempt));
|
||||
archive(cereal::make_nvp("Last update status", last_update_status));
|
||||
archive(cereal::make_nvp("Last update", last_update_time));
|
||||
archive(cereal::make_nvp("Last manifest update", last_manifest_update));
|
||||
try {
|
||||
archive(cereal::make_nvp("Policy version", policy_version));
|
||||
} catch (...) {
|
||||
archive.setNextName(nullptr);
|
||||
}
|
||||
|
||||
archive(cereal::make_nvp("Last policy update", last_policy_update));
|
||||
archive(cereal::make_nvp("Last settings update", last_settings_update));
|
||||
|
||||
// Optional param (upgrade - new parameter)
|
||||
bool is_upgrade_mode = false;
|
||||
try {
|
||||
archive(cereal::make_nvp("Upgrade mode", upgrade_mode));
|
||||
is_upgrade_mode = true;
|
||||
} catch (...) {
|
||||
archive.setNextName(nullptr);
|
||||
}
|
||||
|
||||
if (!is_upgrade_mode) {
|
||||
try {
|
||||
archive(cereal::make_nvp("Update mode", upgrade_mode));
|
||||
} catch (...) {
|
||||
archive.setNextName(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
archive(cereal::make_nvp("Fog address", fog_address));
|
||||
archive(cereal::make_nvp("Registration status", registration_status));
|
||||
archive(cereal::make_nvp("Registration details", registration_details));
|
||||
archive(cereal::make_nvp("Agent ID", agent_id));
|
||||
archive(cereal::make_nvp("Profile ID", profile_id));
|
||||
archive(cereal::make_nvp("Tenant ID", tenant_id));
|
||||
archive(cereal::make_nvp("Manifest status", manifest_status));
|
||||
archive(cereal::make_nvp("Service policy", service_policies));
|
||||
archive(cereal::make_nvp("Service settings", service_settings));
|
||||
}
|
||||
|
||||
private:
|
||||
string last_update_time;
|
||||
string last_update_status;
|
||||
string last_update_attempt;
|
||||
string last_manifest_update;
|
||||
string policy_version;
|
||||
string last_policy_update;
|
||||
string last_settings_update;
|
||||
string upgrade_mode;
|
||||
string fog_address;
|
||||
string registration_status;
|
||||
string manifest_status;
|
||||
string manifest_error;
|
||||
string agent_id;
|
||||
string profile_id;
|
||||
string tenant_id;
|
||||
RegistrationDetails registration_details;
|
||||
map<string, string> service_policies;
|
||||
map<string, string> service_settings;
|
||||
};
|
||||
|
||||
class OrchestrationStatus::Impl : Singleton::Provide<I_OrchestrationStatus>::From<OrchestrationStatus>
|
||||
{
|
||||
public:
|
||||
void
|
||||
writeStatusToFile()
|
||||
{
|
||||
auto orchestrations_status_path = getConfigurationWithDefault<string>(
|
||||
filesystem_prefix + "/conf/orchestrations_status.json",
|
||||
"orchestration",
|
||||
"Orchestration status path"
|
||||
);
|
||||
auto write_result =
|
||||
orchestration_tools->objectToJsonFile<Status>(status, orchestrations_status_path);
|
||||
if (!write_result) {
|
||||
dbgWarning(D_ORCHESTRATOR) << "Failed to write Orchestration status. File: " << orchestrations_status_path;
|
||||
}
|
||||
dbgTrace(D_ORCHESTRATOR) << "Orchestration status file has been updated. File: " << orchestrations_status_path;
|
||||
}
|
||||
|
||||
void
|
||||
recoverFields() override
|
||||
{
|
||||
status.recoverFields();
|
||||
}
|
||||
|
||||
void
|
||||
setServiceConfiguration(
|
||||
const string &service_name,
|
||||
const string &path,
|
||||
const OrchestrationStatusConfigType &configuration_file_type
|
||||
)
|
||||
{
|
||||
if (shouldPolicyStatusBeIgnored(service_name, path)) return;
|
||||
|
||||
switch (configuration_file_type) {
|
||||
case OrchestrationStatusConfigType::POLICY:
|
||||
status.insertServicePolicy(service_name, path);
|
||||
return;
|
||||
case OrchestrationStatusConfigType::SETTINGS:
|
||||
status.insertServiceSetting(service_name, path);
|
||||
return;
|
||||
case OrchestrationStatusConfigType::MANIFEST:
|
||||
dbgAssert(false) << "Manifest is not a service configuration file type";
|
||||
break;
|
||||
case OrchestrationStatusConfigType::DATA:
|
||||
return;
|
||||
case OrchestrationStatusConfigType::COUNT:
|
||||
break;
|
||||
}
|
||||
dbgAssert(false) << "Unknown configuration file type";
|
||||
}
|
||||
|
||||
void
|
||||
init()
|
||||
{
|
||||
time = Singleton::Consume<I_TimeGet>::by<OrchestrationStatus>();
|
||||
orchestration_tools = Singleton::Consume<I_OrchestrationTools>::by<OrchestrationStatus>();
|
||||
initValues();
|
||||
loadFromFile();
|
||||
|
||||
filesystem_prefix = getFilesystemPathConfig();
|
||||
dbgTrace(D_ORCHESTRATOR)
|
||||
<< "Initializing Orchestration status, file system path prefix: "
|
||||
<< filesystem_prefix;
|
||||
|
||||
map<string, string> service_policies_copy = status.getServicePolicies();
|
||||
for (const pair<string, string> &policy: service_policies_copy) {
|
||||
setServiceConfiguration(policy.first, policy.second, OrchestrationStatusConfigType::POLICY);
|
||||
}
|
||||
|
||||
auto mainloop = Singleton::Consume<I_MainLoop>::by<OrchestrationStatus>();
|
||||
mainloop->addRecurringRoutine(
|
||||
I_MainLoop::RoutineType::Timer,
|
||||
seconds(5),
|
||||
[this] ()
|
||||
{
|
||||
dbgTrace(D_ORCHESTRATOR) << "Write Orchestration status file <co-routine>";
|
||||
writeStatusToFile();
|
||||
},
|
||||
"Write Orchestration status file"
|
||||
);
|
||||
}
|
||||
|
||||
private:
|
||||
void initValues();
|
||||
bool shouldPolicyStatusBeIgnored(const string &service_name, const string &path);
|
||||
|
||||
void
|
||||
loadFromFile()
|
||||
{
|
||||
auto orchestrations_status_path = getConfigurationWithDefault<string>(
|
||||
filesystem_prefix + "/conf/orchestrations_status.json",
|
||||
"orchestration",
|
||||
"Orchestration status path"
|
||||
);
|
||||
Maybe<Status> maybe_status_file =
|
||||
orchestration_tools->jsonFileToObject<Status>(orchestrations_status_path);
|
||||
if (!maybe_status_file.ok()) {
|
||||
dbgTrace(D_ORCHESTRATOR)
|
||||
<< "Failed to load Orchestration status, start with clear status."
|
||||
<< " Error: " << maybe_status_file.getErr();
|
||||
return;
|
||||
}
|
||||
|
||||
status = maybe_status_file.unpack();
|
||||
|
||||
dbgInfo(D_ORCHESTRATOR) << "Orchestration status loaded from file." << " File: " << orchestrations_status_path;
|
||||
}
|
||||
|
||||
const string & getLastUpdateAttempt() const override { return status.getLastUpdateAttempt(); }
|
||||
const string & getUpdateStatus() const override { return status.getUpdateStatus(); }
|
||||
const string & getUpdateTime() const override { return status.getUpdateTime(); }
|
||||
const string & getLastManifestUpdate() const override { return status.getLastManifestUpdate(); }
|
||||
const string & getPolicyVersion() const override { return status.getPolicyVersion(); }
|
||||
const string & getLastPolicyUpdate() const override { return status.getLastPolicyUpdate(); }
|
||||
const string & getLastSettingsUpdate() const override { return status.getLastSettingsUpdate(); }
|
||||
const string & getUpgradeMode() const override { return status.getUpgradeMode(); }
|
||||
const string & getFogAddress() const override { return status.getFogAddress(); }
|
||||
const string & getRegistrationStatus() const override { return status.getRegistrationStatus(); }
|
||||
const string & getAgentId() const override { return status.getAgentId(); }
|
||||
const string & getProfileId() const override { return status.getProfileId(); }
|
||||
const string & getTenantId() const override { return status.getTenantId(); }
|
||||
const string & getManifestStatus() const override { return status.getManifestStatus(); }
|
||||
const string & getManifestError() const override { return status.getManifestError(); }
|
||||
const string getRegistrationDetails() const override { return status.getRegistrationDetails().toString(); }
|
||||
const map<string, string> & getServicePolicies() const override { return status.getServicePolicies(); }
|
||||
const map<string, string> & getServiceSettings() const override { return status.getServiceSettings(); }
|
||||
|
||||
void
|
||||
setIsConfigurationUpdated(EnumArray<OrchestrationStatusConfigType, bool> config_types) override
|
||||
{
|
||||
status.setIsConfigurationUpdated(config_types, time->getLocalTimeStr());
|
||||
}
|
||||
|
||||
void
|
||||
setPolicyVersion(const string &_policy_version) override
|
||||
{
|
||||
status.setPolicyVersion(_policy_version);
|
||||
}
|
||||
|
||||
void
|
||||
setRegistrationStatus(const string &_reg_status) override
|
||||
{
|
||||
status.setRegistrationStatus(_reg_status);
|
||||
}
|
||||
|
||||
void
|
||||
setUpgradeMode(const string &_upgrade_mode) override
|
||||
{
|
||||
status.setUpgradeMode(_upgrade_mode);
|
||||
}
|
||||
|
||||
void
|
||||
setAgentType(const string &_agent_type) override
|
||||
{
|
||||
status.setAgentType(_agent_type);
|
||||
}
|
||||
|
||||
void
|
||||
setAgentDetails(
|
||||
const string &_agent_id,
|
||||
const string &_profile_id,
|
||||
const string &_tenant_id) override
|
||||
{
|
||||
status.setAgentDetails(_agent_id, _profile_id, _tenant_id);
|
||||
}
|
||||
|
||||
void
|
||||
setLastUpdateAttempt() override
|
||||
{
|
||||
status.setLastUpdateAttempt(time->getLocalTimeStr());
|
||||
}
|
||||
|
||||
void
|
||||
setFogAddress(const string &_fog_address) override
|
||||
{
|
||||
status.setFogAddress(_fog_address);
|
||||
}
|
||||
|
||||
void
|
||||
setFieldStatus(
|
||||
const OrchestrationStatusFieldType &field_type_status,
|
||||
const OrchestrationStatusResult &status_result,
|
||||
const string &failure_reason) override
|
||||
{
|
||||
string field_value = status_string_map.at(status_result) + " " + failure_reason;
|
||||
switch (field_type_status) {
|
||||
case OrchestrationStatusFieldType::REGISTRATION:
|
||||
status.setRegistrationStatus(field_value);
|
||||
return;
|
||||
case OrchestrationStatusFieldType::MANIFEST:
|
||||
status.setManifestStatus(field_value);
|
||||
status.setManifestError(failure_reason);
|
||||
return;
|
||||
case OrchestrationStatusFieldType::LAST_UPDATE:
|
||||
if (status_result == OrchestrationStatusResult::SUCCESS) {
|
||||
status.setLastUpdateTime(time->getLocalTimeStr());
|
||||
}
|
||||
if (status.getUpdateStatus() != field_value) {
|
||||
writeStatusToFile();
|
||||
}
|
||||
status.setLastUpdateStatus(field_value);
|
||||
return;
|
||||
case OrchestrationStatusFieldType::COUNT:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
setRegistrationDetails(
|
||||
const string &name,
|
||||
const string &type,
|
||||
const string &platform,
|
||||
const string &arch) override
|
||||
{
|
||||
status.setRegistrationDetails(name, type, platform, arch);
|
||||
}
|
||||
|
||||
OrchestrationStatus::Impl & operator=(OrchestrationStatus::Impl &&from) = default;
|
||||
OrchestrationStatus::Impl & operator=(const OrchestrationStatus::Impl &from) = default;
|
||||
|
||||
const map<OrchestrationStatusResult, string> status_string_map = {
|
||||
{ OrchestrationStatusResult::SUCCESS, "Succeeded" },
|
||||
{ OrchestrationStatusResult::FAILED, "Failed. Reason:" }
|
||||
};
|
||||
|
||||
Status status;
|
||||
I_TimeGet *time;
|
||||
I_OrchestrationTools *orchestration_tools;
|
||||
string filesystem_prefix;
|
||||
|
||||
};
|
||||
|
||||
void
|
||||
OrchestrationStatus::Impl::initValues()
|
||||
{
|
||||
status.initValues();
|
||||
}
|
||||
|
||||
bool
|
||||
OrchestrationStatus::Impl::shouldPolicyStatusBeIgnored(
|
||||
const string &service_name,
|
||||
const string &path)
|
||||
{
|
||||
vector<string> default_status_ingored_policies = {
|
||||
"rules",
|
||||
"zones",
|
||||
"triggers",
|
||||
"parameters",
|
||||
"orchestration",
|
||||
"webUserResponse",
|
||||
"kubernetescalico",
|
||||
"activeContextConfig"
|
||||
};
|
||||
|
||||
auto status_ingored_policies = getSettingWithDefault<vector<string>>(
|
||||
default_status_ingored_policies,
|
||||
"orchestration",
|
||||
"Orchestration status ignored policies"
|
||||
);
|
||||
|
||||
auto config_content = orchestration_tools->readFile(path);
|
||||
|
||||
if (!config_content.ok() || config_content.unpack().empty()) {
|
||||
dbgDebug(D_ORCHESTRATOR) << "Can not read the policy for " << service_name;
|
||||
return true;
|
||||
}
|
||||
|
||||
auto find_exist_iterator = status.getServicePolicies().find(service_name);
|
||||
auto find_ignored_iterator = find(status_ingored_policies.begin(), status_ingored_policies.end(), service_name);
|
||||
|
||||
if (config_content.unpack() == "{}") {
|
||||
dbgDebug(D_ORCHESTRATOR) << "Skipping status print for an empty policy file. Policy name: " << service_name;
|
||||
if (find_exist_iterator != status.getServicePolicies().end()) {
|
||||
status.eraseServicePolicy(service_name);
|
||||
}
|
||||
return true;
|
||||
} else if (find_ignored_iterator != status_ingored_policies.end()) {
|
||||
dbgDebug(D_ORCHESTRATOR)
|
||||
<< "Skipping status print for the policy from a list of ignored policies. Policy name: "
|
||||
<< service_name;
|
||||
if (find_exist_iterator != status.getServicePolicies().end()) {
|
||||
status.eraseServicePolicy(service_name);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
OrchestrationStatus::init() { pimpl->init(); }
|
||||
|
||||
OrchestrationStatus::OrchestrationStatus() : Component("OrchestrationStatus"), pimpl(make_unique<Impl>()) {}
|
||||
|
||||
OrchestrationStatus::~OrchestrationStatus() {}
|
||||
|
||||
SASAL_END
|
||||
133
components/security_apps/orchestration/modules/package.cc
Executable file
133
components/security_apps/orchestration/modules/package.cc
Executable file
@@ -0,0 +1,133 @@
|
||||
// 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 "package.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "sasal.h"
|
||||
|
||||
SASAL_START // Orchestration - Modules
|
||||
|
||||
using namespace std;
|
||||
using namespace cereal;
|
||||
|
||||
const map<string, Package::ChecksumTypes> checksumMap = {
|
||||
{ "sha1sum", Package::ChecksumTypes::SHA1 },
|
||||
{ "sha256sum", Package::ChecksumTypes::SHA256 },
|
||||
{ "sha512sum", Package::ChecksumTypes::SHA512 },
|
||||
{ "md5sum", Package::ChecksumTypes::MD5 },
|
||||
};
|
||||
|
||||
const map<string, Package::PackageType> packageTypeMap = {
|
||||
{ "service", Package::PackageType::Service },
|
||||
{ "shared objects", Package::PackageType::SharedObject },
|
||||
};
|
||||
|
||||
bool
|
||||
Package::operator==(const Package &other) const
|
||||
{
|
||||
return checksum_type == other.getChecksumType() && checksum_value == other.getChecksum();
|
||||
}
|
||||
|
||||
bool
|
||||
Package::operator!=(const Package &other) const
|
||||
{
|
||||
return !((*this) == other);
|
||||
}
|
||||
|
||||
void
|
||||
Package::serialize(JSONOutputArchive & out_archive) const
|
||||
{
|
||||
string type = mapTypeToString<PackageType>(package_type, packageTypeMap);
|
||||
string checksum_type_as_string = mapTypeToString<ChecksumTypes>(checksum_type, checksumMap);
|
||||
out_archive(make_nvp("download-path", download_path));
|
||||
out_archive(make_nvp("relative-path", relative_path));
|
||||
out_archive(make_nvp("version", version));
|
||||
out_archive(make_nvp("name", name));
|
||||
out_archive(make_nvp("checksum-type", checksum_type_as_string));
|
||||
out_archive(make_nvp("checksum", checksum_value));
|
||||
out_archive(make_nvp("package-type", type));
|
||||
|
||||
if (require_packages.size() > 0) {
|
||||
out_archive(make_nvp("require", require_packages));
|
||||
}
|
||||
|
||||
if (!installable.ok()) {
|
||||
out_archive(make_nvp("status", installable.ok()));
|
||||
out_archive(make_nvp("message", installable.getErr()));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Package::serialize(JSONInputArchive & in_archive)
|
||||
{
|
||||
string type;
|
||||
string checksum_type_as_string;
|
||||
in_archive(make_nvp("download-path", download_path));
|
||||
in_archive(make_nvp("version", version));
|
||||
in_archive(make_nvp("name", name));
|
||||
in_archive(make_nvp("checksum-type", checksum_type_as_string));
|
||||
in_archive(make_nvp("checksum", checksum_value));
|
||||
in_archive(make_nvp("package-type", type));
|
||||
|
||||
try {
|
||||
in_archive(make_nvp("relative-path", relative_path));
|
||||
} catch (...) {
|
||||
in_archive.setNextName(nullptr);
|
||||
}
|
||||
|
||||
try {
|
||||
in_archive(make_nvp("require", require_packages));
|
||||
} catch (...) {
|
||||
in_archive.setNextName(nullptr);
|
||||
}
|
||||
|
||||
bool is_installable = true;
|
||||
try {
|
||||
in_archive(make_nvp("status", is_installable));
|
||||
} catch (...) {
|
||||
in_archive.setNextName(nullptr);
|
||||
}
|
||||
|
||||
if (!is_installable) {
|
||||
string error_message;
|
||||
try {
|
||||
in_archive(make_nvp("message", error_message));
|
||||
} catch (...) {
|
||||
in_archive.setNextName(nullptr);
|
||||
}
|
||||
installable = genError(error_message);
|
||||
}
|
||||
|
||||
for (auto &character : name) {
|
||||
// Name Validation: should include only: decimal digit / letter / '.' / '_' / '-'
|
||||
if (!isalnum(character) && character != '.' && character != '_' && character != '-') {
|
||||
throw Exception(name + " is invalid package name");
|
||||
}
|
||||
}
|
||||
|
||||
auto checksum_type_value = checksumMap.find(checksum_type_as_string);
|
||||
if (checksum_type_value == checksumMap.end()) {
|
||||
throw Exception(checksum_type_as_string + " isn't a valid checksum type at " + name);
|
||||
}
|
||||
checksum_type = checksum_type_value->second;
|
||||
|
||||
auto package_type_value = packageTypeMap.find(type);
|
||||
if (package_type_value == packageTypeMap.end()) {
|
||||
throw Exception(checksum_type_as_string + " isn't a valid package type at " + name);
|
||||
}
|
||||
package_type = package_type_value->second;
|
||||
}
|
||||
|
||||
SASAL_END
|
||||
149
components/security_apps/orchestration/modules/url_parser.cc
Executable file
149
components/security_apps/orchestration/modules/url_parser.cc
Executable file
@@ -0,0 +1,149 @@
|
||||
// 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 "url_parser.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "singleton.h"
|
||||
#include "common.h"
|
||||
#include "maybe_res.h"
|
||||
#include "sasal.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
SASAL_START // Orchestration - Modules
|
||||
|
||||
USE_DEBUG_FLAG(D_ORCHESTRATOR);
|
||||
|
||||
ostream &
|
||||
operator<<(ostream &os, const URLParser &url)
|
||||
{
|
||||
return os << url.toString();
|
||||
}
|
||||
|
||||
ostream &
|
||||
operator<<(ostream &os, const URLProtocol &protocol)
|
||||
{
|
||||
switch(protocol) {
|
||||
case URLProtocol::HTTP: {
|
||||
return os << "http://";
|
||||
}
|
||||
case URLProtocol::HTTPS: {
|
||||
return os << "https://";
|
||||
}
|
||||
case URLProtocol::LOCAL_FILE: {
|
||||
return os << "file://";
|
||||
}
|
||||
default: {
|
||||
dbgAssert(false) << "Unsupported protocol " << static_cast<unsigned int>(protocol);
|
||||
return os;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
URLParser::URLParser(const string &url)
|
||||
{
|
||||
parseURL(url);
|
||||
}
|
||||
|
||||
Maybe<string>
|
||||
URLParser::getBaseURL() const
|
||||
{
|
||||
if (base_url.empty()) return genError("Error: URL not found");
|
||||
return base_url;
|
||||
}
|
||||
|
||||
void
|
||||
URLParser::parseURL(const string &url)
|
||||
{
|
||||
string url_builder;
|
||||
protocol = parseProtocol(url);
|
||||
switch(protocol) {
|
||||
case URLProtocol::HTTP: {
|
||||
dbgDebug(D_ORCHESTRATOR) << "Protocol of " << url << " is HTTP";
|
||||
port = "80";
|
||||
over_ssl = false;
|
||||
url_builder = url.substr(7);
|
||||
break;
|
||||
}
|
||||
case URLProtocol::HTTPS: {
|
||||
dbgDebug(D_ORCHESTRATOR) << "Protocol of " << url << " is HTTPS";
|
||||
if (url.find("https://") != string::npos) {
|
||||
url_builder = url.substr(8);
|
||||
} else {
|
||||
url_builder = url;
|
||||
}
|
||||
port = "443";
|
||||
over_ssl = true;
|
||||
break;
|
||||
}
|
||||
case URLProtocol::LOCAL_FILE: {
|
||||
dbgDebug(D_ORCHESTRATOR) << "Protocol of " << url << " is local file.";
|
||||
base_url = url.substr(7);
|
||||
return;
|
||||
}
|
||||
default: {
|
||||
dbgAssert(false) << "URL protocol is not supported. Protocol: " << static_cast<unsigned int>(protocol);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
size_t link_extension_position = url_builder.find_first_of("/");
|
||||
if (link_extension_position != string::npos) {
|
||||
query = url_builder.substr(link_extension_position);
|
||||
url_builder = url_builder.substr(0, link_extension_position);
|
||||
}
|
||||
|
||||
size_t port_position = url_builder.find_last_of(":");
|
||||
string link = url_builder;
|
||||
if (port_position != string::npos) {
|
||||
link = url_builder.substr(0, port_position);
|
||||
port = url_builder.substr(port_position + 1);
|
||||
}
|
||||
|
||||
if (!link.empty()) base_url = link;
|
||||
if (!query.empty() && query.back() == '/') query.pop_back();
|
||||
}
|
||||
|
||||
URLProtocol
|
||||
URLParser::parseProtocol(const string &url) const
|
||||
{
|
||||
if (url.find("http://") != string::npos) {
|
||||
return URLProtocol::HTTP;
|
||||
} else if (url.find("https://") != string::npos) {
|
||||
return URLProtocol::HTTPS;
|
||||
} else if (url.find("file://") != string::npos){
|
||||
return URLProtocol::LOCAL_FILE;
|
||||
}
|
||||
|
||||
dbgWarning(D_ORCHESTRATOR)
|
||||
<< "No supported protocol in URL, HTTPS default value is used. URL: " << url;
|
||||
return URLProtocol::HTTPS;
|
||||
}
|
||||
|
||||
void
|
||||
URLParser::setQuery(const string &new_query)
|
||||
{
|
||||
query = new_query;
|
||||
}
|
||||
|
||||
string
|
||||
URLParser::toString() const
|
||||
{
|
||||
stringstream s_build;
|
||||
s_build << protocol << base_url << query << ":" << port;
|
||||
return s_build.str();
|
||||
}
|
||||
|
||||
SASAL_END
|
||||
1629
components/security_apps/orchestration/orchestration_comp.cc
Executable file
1629
components/security_apps/orchestration/orchestration_comp.cc
Executable file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,5 @@
|
||||
ADD_DEFINITIONS(-Wno-deprecated-declarations)
|
||||
|
||||
add_library(orchestration_tools orchestration_tools.cc)
|
||||
|
||||
add_subdirectory(orchestration_tools_ut)
|
||||
@@ -0,0 +1,475 @@
|
||||
// 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 "orchestration_tools.h"
|
||||
|
||||
#include "openssl/md5.h"
|
||||
#include "openssl/sha.h"
|
||||
#include "cereal/external/rapidjson/document.h"
|
||||
#include "cereal/types/vector.hpp"
|
||||
#include "cereal/types/set.hpp"
|
||||
|
||||
#include <netdb.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "sasal.h"
|
||||
|
||||
SASAL_START // Orchestration - Tools
|
||||
|
||||
using namespace std;
|
||||
using namespace rapidjson;
|
||||
|
||||
static const string base64_base_str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
class OrchestrationTools::Impl : Singleton::Provide<I_OrchestrationTools>::From<OrchestrationTools>
|
||||
{
|
||||
public:
|
||||
bool packagesToJsonFile(const map<packageName, Package> &packages, const string &path) const override;
|
||||
Maybe<map<packageName, Package>> loadPackagesFromJson(const string &path) const override;
|
||||
|
||||
Maybe<map<packageName, packageDetails>>
|
||||
jsonObjectSplitter(
|
||||
const string &json,
|
||||
const string &tenant_id) const override;
|
||||
|
||||
Maybe<string> readFile(const string &path) const override;
|
||||
bool writeFile(const string &text, const string &path) const override;
|
||||
bool removeFile(const string &path) const override;
|
||||
bool copyFile(const string &src_path, const string &dst_path) const override;
|
||||
bool doesFileExist(const string &file_path) const override;
|
||||
bool createDirectory(const string &directory_path) const override;
|
||||
bool doesDirectoryExist(const string &dir_path) const override;
|
||||
bool executeCmd(const string &cmd) const override;
|
||||
bool isNonEmptyFile(const string &path) const override;
|
||||
|
||||
Maybe<string> calculateChecksum(Package::ChecksumTypes checksum_type, const string &path) const override;
|
||||
|
||||
string base64Encode(const string &input) const override;
|
||||
string base64Decode(const string &input) const override;
|
||||
|
||||
private:
|
||||
string calculateFileMd5(ifstream &file) const;
|
||||
string calculateSHA256Sum(ifstream &file) const;
|
||||
string calculateSHA1Sum(ifstream &file) const;
|
||||
string calculateSHA512Sum(ifstream &file) const;
|
||||
};
|
||||
|
||||
using packageName = I_OrchestrationTools::packageName;
|
||||
using packageDetails = I_OrchestrationTools::packageDetails;
|
||||
|
||||
static bool
|
||||
checkExistence(const string &path, bool is_dir)
|
||||
{
|
||||
try {
|
||||
struct stat info;
|
||||
if (stat(path.c_str(), &info) != 0) return false;
|
||||
int flag = is_dir ? S_IFDIR : S_IFREG;
|
||||
return info.st_mode & flag;
|
||||
} catch (exception &e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
OrchestrationTools::Impl::doesFileExist(const string &file_path) const
|
||||
{
|
||||
return checkExistence(file_path, false);
|
||||
}
|
||||
|
||||
bool
|
||||
OrchestrationTools::Impl::doesDirectoryExist(const string &dir_path) const
|
||||
{
|
||||
return checkExistence(dir_path, true);
|
||||
}
|
||||
|
||||
bool
|
||||
OrchestrationTools::Impl::writeFile(const string &text, const string &path) const
|
||||
{
|
||||
dbgDebug(D_ORCHESTRATOR) << "Writing file: text = " << text << ", path = " << path;
|
||||
if (path.find('/') != string::npos) {
|
||||
string dir_path = path.substr(0, path.find_last_of('/'));
|
||||
if (!createDirectory(dir_path)) {
|
||||
dbgDebug(D_ORCHESTRATOR) << "Failed to write file because directory creation failed. file: "
|
||||
<< path;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
try {
|
||||
ofstream fout(path);
|
||||
fout << text;
|
||||
return true;
|
||||
} catch (const ofstream::failure &e) {
|
||||
dbgDebug(D_ORCHESTRATOR) << "Error while writing file in " << path << ", " << e.what();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
OrchestrationTools::Impl::isNonEmptyFile(const string &path) const
|
||||
{
|
||||
if (!doesFileExist(path)) {
|
||||
dbgDebug(D_ORCHESTRATOR) << "Cannot read file, file does not exist. File: " << path;
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
ifstream text_file(path);
|
||||
if (!text_file) {
|
||||
dbgDebug(D_ORCHESTRATOR) << "Cannot open file. File: " << path;
|
||||
return false;
|
||||
}
|
||||
|
||||
char buf[1];
|
||||
text_file.read(buf, 1);
|
||||
return text_file.gcount() != 0;
|
||||
} catch (const ifstream::failure &e) {
|
||||
dbgDebug(D_ORCHESTRATOR) << "Error while reading file " << path << ", " << e.what();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Maybe<string>
|
||||
OrchestrationTools::Impl::readFile(const string &path) const
|
||||
{
|
||||
if (!doesFileExist(path)) {
|
||||
dbgDebug(D_ORCHESTRATOR) << "Cannot read file, file does not exist. File: " << path;
|
||||
return genError("File " + path + " does not exist.");
|
||||
}
|
||||
try {
|
||||
ifstream text_file(path);
|
||||
if (!text_file) {
|
||||
return genError("Cannot open file. File: " + path);
|
||||
}
|
||||
stringstream buffer;
|
||||
buffer << text_file.rdbuf();
|
||||
return buffer.str();
|
||||
} catch (const ifstream::failure &e) {
|
||||
dbgDebug(D_ORCHESTRATOR) << "Error while reading file " << path << ", " << e.what();
|
||||
return genError("Error while reading file " + path + ", " + e.what());
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
OrchestrationTools::Impl::removeFile(const string &path) const
|
||||
{
|
||||
if (remove(path.c_str()) != 0) {
|
||||
dbgDebug(D_ORCHESTRATOR) << "Error deleting file. File: " << path;
|
||||
return false;
|
||||
} else {
|
||||
dbgDebug(D_ORCHESTRATOR) << "Successfully deleted the file " << path;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Maybe<string>
|
||||
OrchestrationTools::Impl::calculateChecksum(Package::ChecksumTypes checksum_type, const string &path) const
|
||||
{
|
||||
if (!doesFileExist(path)) {
|
||||
dbgDebug(D_ORCHESTRATOR) << "Cannot read file, file does not exist. File: " << path;
|
||||
return genError("File " + path + " does not exist.");
|
||||
}
|
||||
try {
|
||||
ifstream file(path);
|
||||
if (!file) {
|
||||
return genError("Cannot open file. File: " + path);
|
||||
}
|
||||
|
||||
switch (checksum_type) {
|
||||
case Package::ChecksumTypes::MD5:
|
||||
return calculateFileMd5(file);
|
||||
case Package::ChecksumTypes::SHA256:
|
||||
return calculateSHA256Sum(file);
|
||||
case Package::ChecksumTypes::SHA1:
|
||||
return calculateSHA1Sum(file);
|
||||
case Package::ChecksumTypes::SHA512:
|
||||
return calculateSHA512Sum(file);
|
||||
}
|
||||
} catch (const ifstream::failure &e) {
|
||||
dbgDebug(D_ORCHESTRATOR) << "Error while reading file " << path << ", " << e.what();
|
||||
return genError("Error while reading file " + path + ", " + e.what());
|
||||
}
|
||||
|
||||
dbgAssert(false) << "Checksum type is not supported. Checksum type: " << static_cast<unsigned int>(checksum_type);
|
||||
return genError("Unsupported checksum type");
|
||||
}
|
||||
|
||||
bool
|
||||
OrchestrationTools::Impl::copyFile(const string &src_path, const string &dst_path) const
|
||||
{
|
||||
if (!doesFileExist(src_path)) {
|
||||
dbgDebug(D_ORCHESTRATOR) << "Failed to copy file. File does not exist: " << src_path;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (src_path.compare(dst_path) == 0) {
|
||||
dbgDebug(D_ORCHESTRATOR) << "Source path is equal to the destination path. Path: " << src_path;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (dst_path.find('/') != string::npos) {
|
||||
string dir_path = dst_path.substr(0, dst_path.find_last_of('/'));
|
||||
if (!createDirectory(dir_path)) {
|
||||
dbgDebug(D_ORCHESTRATOR) << "Failed to copy file. Directory creation failed: " << dir_path;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
ifstream src(src_path, ios::binary);
|
||||
ofstream dest(dst_path, ios::binary);
|
||||
dest << src.rdbuf();
|
||||
return true;
|
||||
} catch (const ios_base::failure &e) {
|
||||
dbgDebug(D_ORCHESTRATOR) << "Failed to copy file " << src_path << " to " << dst_path << ", " << e.what();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Maybe<map<packageName, packageDetails>>
|
||||
OrchestrationTools::Impl::jsonObjectSplitter(const string &json, const string &tenant_id) const
|
||||
{
|
||||
Document document;
|
||||
map<string, string> parsed;
|
||||
|
||||
document.Parse(json.c_str());
|
||||
if (document.HasParseError()) return genError("JSON file is not valid.");
|
||||
|
||||
for (Value::MemberIterator itr = document.MemberBegin(); itr != document.MemberEnd(); ++itr) {
|
||||
|
||||
if (!tenant_id.empty() && itr->value.IsObject()) {
|
||||
|
||||
itr->value.AddMember(
|
||||
Value("tenantID"),
|
||||
Value(tenant_id.c_str(), tenant_id.size()),
|
||||
document.GetAllocator()
|
||||
);
|
||||
}
|
||||
|
||||
rapidjson::StringBuffer buffer;
|
||||
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
|
||||
itr->value.Accept(writer);
|
||||
parsed.insert({itr->name.GetString(), buffer.GetString()});
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
|
||||
bool
|
||||
OrchestrationTools::Impl::packagesToJsonFile(const map<packageName, Package> &packages, const string &path) const
|
||||
{
|
||||
try {
|
||||
ofstream os(path);
|
||||
cereal::JSONOutputArchive archive_out(os);
|
||||
vector<Package> packges_vector;
|
||||
for (auto p: packages) {
|
||||
packges_vector.push_back(p.second);
|
||||
}
|
||||
archive_out(cereal::make_nvp("packages", packges_vector));
|
||||
} catch (cereal::Exception &e) {
|
||||
dbgDebug(D_ORCHESTRATOR) << "Failed to write vector of packages to JSON file " << path << ", " << e.what();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Maybe<map<packageName, Package>>
|
||||
OrchestrationTools::Impl::loadPackagesFromJson(const string &path) const
|
||||
{
|
||||
dbgDebug(D_ORCHESTRATOR) << "Parsing packages from " << path;
|
||||
try {
|
||||
ifstream is(path);
|
||||
cereal::JSONInputArchive archive_in(is);
|
||||
vector<Package> packages_vector;
|
||||
archive_in(packages_vector);
|
||||
map<packageName, Package> packages;
|
||||
for (auto p: packages_vector) {
|
||||
packages[p.getName()] = p;
|
||||
}
|
||||
return packages;
|
||||
} catch (const exception &e) {
|
||||
dbgDebug(D_ORCHESTRATOR) << "Failed to load vector of packages from JSON file " << path << ", " << e.what();
|
||||
return genError(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
OrchestrationTools::Impl::createDirectory(const string &directory_path) const
|
||||
{
|
||||
string dir;
|
||||
struct stat info;
|
||||
for (size_t i = 0; i < directory_path.size(); i++) {
|
||||
dir.push_back(directory_path[i]);
|
||||
if (directory_path[i] == '/' || i + 1 == directory_path.size()) {
|
||||
if (stat(dir.c_str(), &info) != 0) {
|
||||
if(mkdir(dir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)) {
|
||||
dbgDebug(D_ORCHESTRATOR) << "Failed to create directory " << directory_path;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
OrchestrationTools::Impl::executeCmd(const string &cmd) const
|
||||
{
|
||||
int ret = system(cmd.c_str());
|
||||
if (ret != 0) {
|
||||
dbgDebug(D_ORCHESTRATOR) << "System command failed, " + cmd;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
string
|
||||
OrchestrationTools::Impl::calculateFileMd5(ifstream &file) const
|
||||
{
|
||||
MD5_CTX md5_Context;
|
||||
MD5_Init(&md5_Context);
|
||||
|
||||
char read_buf[512];
|
||||
while (file) {
|
||||
file.read(read_buf, 512);
|
||||
auto size = file.gcount();
|
||||
if (!size) break;
|
||||
MD5_Update(&md5_Context, read_buf, size);
|
||||
}
|
||||
|
||||
unsigned char digest[16];
|
||||
MD5_Final(digest, &md5_Context);
|
||||
|
||||
stringstream out;
|
||||
for (int i = 0; i < MD5_DIGEST_LENGTH; ++i) {
|
||||
out << setfill('0') << setw(2) << hex << (unsigned int)digest[i];
|
||||
}
|
||||
return out.str();
|
||||
}
|
||||
|
||||
string
|
||||
OrchestrationTools::Impl::calculateSHA256Sum(ifstream &file) const
|
||||
{
|
||||
unsigned char hash[SHA256_DIGEST_LENGTH];
|
||||
SHA256_CTX sha256;
|
||||
SHA256_Init(&sha256);
|
||||
|
||||
char read_buf[512];
|
||||
while (file) {
|
||||
file.read(read_buf, 512);
|
||||
auto size = file.gcount();
|
||||
if (!size) break;
|
||||
SHA256_Update(&sha256, read_buf, size);
|
||||
}
|
||||
|
||||
SHA256_Final(hash, &sha256);
|
||||
stringstream string_stream;
|
||||
for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) {
|
||||
string_stream << hex << setw(2) << setfill('0') << (int)hash[i];
|
||||
}
|
||||
return string_stream.str();
|
||||
}
|
||||
|
||||
string
|
||||
OrchestrationTools::Impl::calculateSHA1Sum(ifstream &file) const
|
||||
{
|
||||
unsigned char hash[SHA_DIGEST_LENGTH];
|
||||
SHA_CTX sha1;
|
||||
SHA1_Init(&sha1);
|
||||
|
||||
char read_buf[512];
|
||||
while (file) {
|
||||
file.read(read_buf, 512);
|
||||
auto size = file.gcount();
|
||||
if (!size) break;
|
||||
SHA1_Update(&sha1, read_buf, size);
|
||||
}
|
||||
|
||||
SHA1_Final(hash, &sha1);
|
||||
stringstream string_stream;
|
||||
for (int i = 0; i < SHA_DIGEST_LENGTH; i++) {
|
||||
string_stream << hex << setw(2) << setfill('0') << (int)hash[i];
|
||||
}
|
||||
return string_stream.str();
|
||||
}
|
||||
|
||||
string
|
||||
OrchestrationTools::Impl::calculateSHA512Sum(ifstream &file) const
|
||||
{
|
||||
unsigned char hash[SHA512_DIGEST_LENGTH];
|
||||
SHA512_CTX sha512;
|
||||
SHA512_Init(&sha512);
|
||||
|
||||
char read_buf[512];
|
||||
while (file) {
|
||||
file.read(read_buf, 512);
|
||||
auto size = file.gcount();
|
||||
if (!size) break;
|
||||
SHA512_Update(&sha512, read_buf, size);
|
||||
}
|
||||
|
||||
SHA512_Final(hash, &sha512);
|
||||
stringstream string_stream;
|
||||
for (int i = 0; i < SHA512_DIGEST_LENGTH; i++) {
|
||||
string_stream << hex << setw(2) << setfill('0') << (int)hash[i];
|
||||
}
|
||||
return string_stream.str();
|
||||
}
|
||||
|
||||
string
|
||||
OrchestrationTools::Impl::base64Encode(const string &input) const
|
||||
{
|
||||
string out;
|
||||
int val = 0, val_base = -6;
|
||||
for (unsigned char c : input) {
|
||||
val = (val << 8) + c;
|
||||
val_base += 8;
|
||||
while (val_base >= 0) {
|
||||
out.push_back(base64_base_str[(val >> val_base) & 0x3F]);
|
||||
val_base -= 6;
|
||||
}
|
||||
}
|
||||
// -6 indicates the number of bits to take from each character
|
||||
// (6 bits is enough to present a range of 0 to 63)
|
||||
if (val_base > -6) out.push_back(base64_base_str[((val << 8) >> (val_base + 8)) & 0x3F]);
|
||||
while (out.size() % 4) out.push_back('=');
|
||||
return out;
|
||||
}
|
||||
|
||||
string
|
||||
OrchestrationTools::Impl::base64Decode(const string &input) const
|
||||
{
|
||||
string out;
|
||||
vector<int> T(256, -1);
|
||||
for (int i = 0; i < 64; i++) {
|
||||
T[base64_base_str[i]] = i;
|
||||
}
|
||||
|
||||
int val = 0, valb = -8;
|
||||
for (unsigned char c : input) {
|
||||
if (T[c] == -1) break;
|
||||
val = (val << 6) + T[c];
|
||||
valb += 6;
|
||||
if (valb >= 0) {
|
||||
out.push_back(char((val >> valb) & 0xFF));
|
||||
valb -= 8;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
OrchestrationTools::OrchestrationTools() : Component("OrchestrationTools"), pimpl(make_unique<Impl>()) {}
|
||||
|
||||
OrchestrationTools::~OrchestrationTools() {}
|
||||
|
||||
SASAL_END
|
||||
@@ -0,0 +1,7 @@
|
||||
link_directories(${ng_module_osrc_openssl_path}/lib)
|
||||
|
||||
add_unit_test(
|
||||
orchestration_tools_ut
|
||||
"orchestration_tools_ut.cc"
|
||||
"orchestration_modules;orchestration_tools;singleton;-lcrypto;"
|
||||
)
|
||||
@@ -0,0 +1,263 @@
|
||||
#include "orchestration_tools.h"
|
||||
|
||||
#include "cptest.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace testing;
|
||||
|
||||
class OrchestrationToolsTest : public Test
|
||||
{
|
||||
public:
|
||||
OrchestrationToolsTest() : manifest_file("manifest.json")
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
cleanSpaces(string &str)
|
||||
{
|
||||
str.erase(remove(str.begin(), str.end(), ' '), str.end());
|
||||
}
|
||||
|
||||
OrchestrationTools orchestration_tools;
|
||||
I_OrchestrationTools *i_orchestration_tools = Singleton::Consume<I_OrchestrationTools>::from(orchestration_tools);
|
||||
string manifest_file = "manifest.json";
|
||||
string manifest_text = "{"
|
||||
" \"packages\": ["
|
||||
" {"
|
||||
" \"download-path\": \"https://a/install_orchestration.sh\","
|
||||
" \"relative-path\": \"/install_orchestration.sh\","
|
||||
" \"name\": \"l4_firewall\","
|
||||
" \"version\": \"b\","
|
||||
" \"checksum-type\": \"sha1sum\","
|
||||
" \"checksum\": \"206afe939eb53168d70fbb777afb4e814097c4dc\","
|
||||
" \"package-type\": \"service\","
|
||||
" \"require\": []"
|
||||
" },"
|
||||
" {"
|
||||
" \"name\": \"orchestration\","
|
||||
" \"download-path\": \"https://a/install_orchestration.sh\","
|
||||
" \"relative-path\": \"/install_orchestration.sh\","
|
||||
" \"version\": \"c\","
|
||||
" \"checksum-type\": \"md5sum\","
|
||||
" \"checksum\": \"04417eef36f93cec4ca7a435bdcd004508dbaa83\","
|
||||
" \"package-type\": \"service\","
|
||||
" \"require\": []"
|
||||
" }"
|
||||
" ]"
|
||||
"}";
|
||||
};
|
||||
|
||||
TEST_F(OrchestrationToolsTest, doNothing)
|
||||
{
|
||||
}
|
||||
|
||||
TEST_F(OrchestrationToolsTest, writeReadTextToFile)
|
||||
{
|
||||
EXPECT_TRUE(i_orchestration_tools->writeFile(manifest_text, manifest_file));
|
||||
EXPECT_TRUE(i_orchestration_tools->doesFileExist(manifest_file));
|
||||
EXPECT_TRUE(i_orchestration_tools->isNonEmptyFile(manifest_file));
|
||||
EXPECT_EQ(manifest_text, i_orchestration_tools->readFile(manifest_file).unpack());
|
||||
|
||||
EXPECT_FALSE(i_orchestration_tools->isNonEmptyFile("no_such_file"));
|
||||
}
|
||||
|
||||
TEST_F(OrchestrationToolsTest, loadPackagesFromJsonTest)
|
||||
{
|
||||
EXPECT_TRUE(i_orchestration_tools->writeFile("blabla", "in_test.json"));
|
||||
string file_name = "in_test.json";
|
||||
Maybe<map<string, Package>> packages = i_orchestration_tools->loadPackagesFromJson(file_name);
|
||||
EXPECT_FALSE(packages.ok());
|
||||
|
||||
Maybe<string> value = i_orchestration_tools->readFile(manifest_file);
|
||||
packages = i_orchestration_tools->loadPackagesFromJson(manifest_file);
|
||||
EXPECT_TRUE(packages.ok());
|
||||
EXPECT_EQ(2u, packages.unpack().size());
|
||||
EXPECT_TRUE(packages.unpack().find("orchestration") != packages.unpack().end());
|
||||
EXPECT_TRUE(packages.unpack().find("l4_firewall") != packages.unpack().end());
|
||||
EXPECT_TRUE(packages.unpack().find("Hello World") == packages.unpack().end());
|
||||
}
|
||||
|
||||
TEST_F(OrchestrationToolsTest, copyFile)
|
||||
{
|
||||
EXPECT_TRUE(i_orchestration_tools->writeFile("blabla", "in_test.json"));
|
||||
EXPECT_TRUE(i_orchestration_tools->copyFile("in_test.json", "cpy_test.json"));
|
||||
EXPECT_EQ("blabla", i_orchestration_tools->readFile("cpy_test.json").unpack());
|
||||
EXPECT_FALSE(i_orchestration_tools->copyFile("NOT_EXISTS_FILE", "cpy2_test.json"));
|
||||
auto read_unexists_file = i_orchestration_tools->readFile("cpy2_test.json");
|
||||
EXPECT_FALSE(read_unexists_file.ok());
|
||||
EXPECT_THAT(read_unexists_file, IsError("File cpy2_test.json does not exist."));
|
||||
}
|
||||
|
||||
TEST_F(OrchestrationToolsTest, checksumTest)
|
||||
{
|
||||
EXPECT_EQ("df5ea29924d39c3be8785734f13169c6",
|
||||
i_orchestration_tools->calculateChecksum(Package::ChecksumTypes::MD5, "in_test.json").unpack());
|
||||
EXPECT_EQ("ccadd99b16cd3d200c22d6db45d8b6630ef3d936767127347ec8a76ab992c2ea",
|
||||
i_orchestration_tools->calculateChecksum(Package::ChecksumTypes::SHA256, "in_test.json").unpack());
|
||||
EXPECT_EQ("bb21158c733229347bd4e681891e213d94c685be",
|
||||
i_orchestration_tools->calculateChecksum(Package::ChecksumTypes::SHA1, "in_test.json").unpack());
|
||||
EXPECT_EQ("d1c2e12cfeababc8b95daf6902e210b170992e68fd1c1f19565a40cf0099c6e2cb559"
|
||||
"b85d7c14ea05b4dca0a790656d003ccade9286827cffdf8e664fd271499",
|
||||
i_orchestration_tools->calculateChecksum(Package::ChecksumTypes::SHA512, "in_test.json").unpack());
|
||||
EXPECT_NE(
|
||||
"12342",
|
||||
i_orchestration_tools->calculateChecksum(Package::ChecksumTypes::SHA256, "in_test.json").unpack()
|
||||
);
|
||||
}
|
||||
|
||||
TEST_F(OrchestrationToolsTest, removeTestFiles)
|
||||
{
|
||||
EXPECT_TRUE(i_orchestration_tools->doesFileExist(manifest_file));
|
||||
EXPECT_TRUE(i_orchestration_tools->removeFile(manifest_file));
|
||||
EXPECT_FALSE(i_orchestration_tools->doesFileExist(manifest_file));
|
||||
|
||||
EXPECT_TRUE(i_orchestration_tools->doesFileExist(string("in_test.json")));
|
||||
EXPECT_TRUE(i_orchestration_tools->removeFile(string("in_test.json")));
|
||||
EXPECT_FALSE(i_orchestration_tools->doesFileExist(string("in_test.json")));
|
||||
|
||||
EXPECT_TRUE(i_orchestration_tools->doesFileExist(string("cpy_test.json")));
|
||||
EXPECT_TRUE(i_orchestration_tools->removeFile(string("cpy_test.json")));
|
||||
EXPECT_FALSE(i_orchestration_tools->doesFileExist(string("cpy_test.json")));
|
||||
|
||||
EXPECT_FALSE(i_orchestration_tools->removeFile(string("test.json")));
|
||||
}
|
||||
|
||||
TEST_F(OrchestrationToolsTest, jsonObjectSplitter)
|
||||
{
|
||||
string update_text = "{"
|
||||
" \"manifest\":"
|
||||
" {"
|
||||
" \"checksaum\":\"12e307c8f0aab4f51a160d5fb2396de1ca9da5b9\","
|
||||
" \"download-options\": ["
|
||||
" \"http://172.23.92.135/manifest_file.txt\""
|
||||
" ]"
|
||||
" },"
|
||||
" \"policy\":"
|
||||
" {"
|
||||
" \"checksum\":\"82e307c8f0aab4f51a160d5fb2396de1ca9da5b9\","
|
||||
" \"download-opations\": ["
|
||||
" \"http://172.23.92.135/policy_file.txt\","
|
||||
" \"ftp://172.23.92.135/policy_file.txt\""
|
||||
" ]"
|
||||
" },"
|
||||
" \"version\": \"10\""
|
||||
"}";
|
||||
|
||||
string manifest = "{"
|
||||
" \"checksaum\":\"12e307c8f0aab4f51a160d5fb2396de1ca9da5b9\","
|
||||
" \"download-options\": ["
|
||||
" \"http://172.23.92.135/manifest_file.txt\""
|
||||
" ]"
|
||||
" }";
|
||||
|
||||
string policy = "{"
|
||||
" \"checksum\":\"82e307c8f0aab4f51a160d5fb2396de1ca9da5b9\","
|
||||
" \"download-opations\": ["
|
||||
" \"http://172.23.92.135/policy_file.txt\","
|
||||
" \"ftp://172.23.92.135/policy_file.txt\""
|
||||
" ]"
|
||||
" }";
|
||||
|
||||
Maybe<map<string, string>> parsed = i_orchestration_tools->jsonObjectSplitter(update_text, "");
|
||||
EXPECT_TRUE(parsed.ok());
|
||||
cleanSpaces(manifest);
|
||||
EXPECT_EQ(manifest, parsed.unpack().find("manifest")->second);
|
||||
cleanSpaces(policy);
|
||||
EXPECT_EQ(policy, parsed.unpack().find("policy")->second);
|
||||
string policy_value = parsed.unpack().find("policy")->second;
|
||||
EXPECT_TRUE(policy_value.find("82e307c8f0aab4f51a160d5fb2396de1ca9da5b9") != string::npos);
|
||||
|
||||
string invalid_json = "{"
|
||||
" \"manifest\":"
|
||||
" {"
|
||||
" \"checksaum\":\"12e307c8f0aab4f51a160d5fb2396de1ca9da5b9\","
|
||||
" \"download-options\": ["
|
||||
" \"http://172.23.92.135/manifest_file.txt\""
|
||||
" ]";
|
||||
parsed = i_orchestration_tools->jsonObjectSplitter(invalid_json, "");
|
||||
EXPECT_FALSE(parsed.ok());
|
||||
}
|
||||
|
||||
TEST_F(OrchestrationToolsTest, jsonFileToPackages)
|
||||
{
|
||||
stringstream string_stream;
|
||||
string_stream << "{"
|
||||
" \"packages\": ["
|
||||
" {"
|
||||
" \"download-path\": \"https://a/install_orchestration.sh\","
|
||||
" \"relative-path\": \"/install_orchestration.sh\","
|
||||
" \"name\": \"nano-agent\","
|
||||
" \"version\": \"24452\","
|
||||
" \"checksum-type\": \"sha1sum\","
|
||||
" \"checksum\": \"a58bbab8020b0e6d08568714b5e582a3adf9c805\","
|
||||
" \"package-type\": \"service\","
|
||||
" \"require\": []"
|
||||
" }"
|
||||
" ]"
|
||||
"}";
|
||||
i_orchestration_tools->writeFile(string_stream.str(), "packages_tmp.json");
|
||||
Maybe<map<string, Package>> packages = i_orchestration_tools->loadPackagesFromJson("packages_tmp.json");
|
||||
EXPECT_TRUE(packages.ok());
|
||||
EXPECT_TRUE(packages.unpack().find("nano-agent") != packages.unpack().end());
|
||||
}
|
||||
|
||||
TEST_F(OrchestrationToolsTest, packagesToJsonFile)
|
||||
{
|
||||
stringstream string_stream;
|
||||
string_stream << "{"
|
||||
" \"packages\": ["
|
||||
" {"
|
||||
" \"download-path\": \"https://a/install_orchestration.sh\","
|
||||
" \"relative-path\": \"/install_orchestration.sh\","
|
||||
" \"name\": \"my\","
|
||||
" \"version\": \"c\","
|
||||
" \"checksum-type\": \"sha1sum\","
|
||||
" \"checksum\": \"a58bbab8020b0e6d08568714b5e582a3adf9c805\","
|
||||
" \"package-type\": \"service\","
|
||||
" \"require\": []"
|
||||
" }"
|
||||
" ]"
|
||||
"}";
|
||||
i_orchestration_tools->writeFile(string_stream.str(), "packages.json");
|
||||
Maybe<map<string, Package>> packages = i_orchestration_tools->loadPackagesFromJson("packages.json");
|
||||
EXPECT_TRUE(packages.ok());
|
||||
EXPECT_TRUE(i_orchestration_tools->packagesToJsonFile(packages.unpack(), "packages.json"));
|
||||
auto file_content = i_orchestration_tools->readFile("packages.json").unpack();
|
||||
EXPECT_TRUE(file_content.find("a58bbab8020b0e6d08568714b5e582a3adf9c805") != string::npos);
|
||||
}
|
||||
|
||||
TEST_F(OrchestrationToolsTest, executeCommand)
|
||||
{
|
||||
EXPECT_TRUE(i_orchestration_tools->executeCmd("exit 0"));
|
||||
EXPECT_FALSE(i_orchestration_tools->executeCmd("exit 1"));
|
||||
}
|
||||
|
||||
TEST_F(OrchestrationToolsTest, createDirectory)
|
||||
{
|
||||
string path = "/tmp/temp_dir";
|
||||
EXPECT_TRUE(i_orchestration_tools->createDirectory(path));
|
||||
EXPECT_TRUE(i_orchestration_tools->doesDirectoryExist(path));
|
||||
// get True after the directory already exists
|
||||
EXPECT_TRUE(i_orchestration_tools->createDirectory(path));
|
||||
}
|
||||
|
||||
TEST_F(OrchestrationToolsTest, base64DecodeEncode)
|
||||
{
|
||||
string clear_text = "{\n"
|
||||
" \"token\": \"77f380c5-9397-4e53-bb78-7c9df8f80a03\",\n"
|
||||
" \"expired\": false\n"
|
||||
"}";
|
||||
string base64_text = "ewogICAidG9rZW4iOiAiNzdmMzgwYzUtOTM5Ny00ZTUzLWJiNzgtN2M5Z"\
|
||||
"GY4ZjgwYTAzIiwKICAgImV4cGlyZWQiOiBmYWxzZQp9";
|
||||
EXPECT_EQ(clear_text, i_orchestration_tools->base64Decode(base64_text));
|
||||
EXPECT_EQ(base64_text, i_orchestration_tools->base64Encode(clear_text));
|
||||
|
||||
string test_str = "";
|
||||
EXPECT_EQ(test_str, i_orchestration_tools->base64Decode(i_orchestration_tools->base64Encode(test_str)));
|
||||
test_str = "TEStsr fassaf saf";
|
||||
EXPECT_EQ(test_str, i_orchestration_tools->base64Decode(i_orchestration_tools->base64Encode(test_str)));
|
||||
test_str = "T24122142sfsavs!@!%";
|
||||
EXPECT_EQ(test_str, i_orchestration_tools->base64Decode(i_orchestration_tools->base64Encode(test_str)));
|
||||
test_str = "\nsdlsakdsad\nsdaslds";
|
||||
EXPECT_EQ(test_str, i_orchestration_tools->base64Decode(i_orchestration_tools->base64Encode(test_str)));
|
||||
}
|
||||
15
components/security_apps/orchestration/orchestration_ut/CMakeLists.txt
Executable file
15
components/security_apps/orchestration/orchestration_ut/CMakeLists.txt
Executable file
@@ -0,0 +1,15 @@
|
||||
link_directories(${ng_module_osrc_openssl_path}/lib)
|
||||
link_directories(${ng_module_osrc_curl_path}/lib)
|
||||
link_directories(${BOOST_ROOT}/lib)
|
||||
|
||||
add_unit_test(
|
||||
orchestration_ut
|
||||
"orchestration_ut.cc"
|
||||
"orchestration;rest;manifest_controller;service_controller;orchestration_downloader;agent_details;package_handler;orchestration_modules;orchestration_tools;environment;config;logging;version;shell_cmd;message;update_communication;agent_details_reporter;connkey;encryptor;metric;ip_utilities;event_is;-lcrypto;-lboost_filesystem;-lboost_regex;-lssl"
|
||||
)
|
||||
|
||||
add_unit_test(
|
||||
orchestration_multitenant_ut
|
||||
"orchestration_multitenant_ut.cc"
|
||||
"orchestration;rest;manifest_controller;service_controller;orchestration_downloader;agent_details;package_handler;orchestration_modules;orchestration_tools;environment;config;logging;version;shell_cmd;message;update_communication;agent_details_reporter;connkey;encryptor;metric;ip_utilities;event_is;-lcrypto;-lboost_filesystem;-lboost_regex;-lssl;curl"
|
||||
)
|
||||
@@ -0,0 +1,445 @@
|
||||
#include "orchestration_comp.h"
|
||||
|
||||
#include "cptest.h"
|
||||
#include "mock/mock_encryptor.h"
|
||||
#include "mock/mock_orchestration_tools.h"
|
||||
#include "mock/mock_downloader.h"
|
||||
#include "mock/mock_manifest_controller.h"
|
||||
#include "mock/mock_service_controller.h"
|
||||
#include "mock/mock_orchestration_status.h"
|
||||
#include "mock/mock_update_communication.h"
|
||||
#include "mock/mock_details_resolver.h"
|
||||
#include "mock/mock_agent_details_reporter.h"
|
||||
#include "mock/mock_logging.h"
|
||||
#include "mock/mock_shell_cmd.h"
|
||||
#include "mock/mock_mainloop.h"
|
||||
#include "mock/mock_messaging.h"
|
||||
#include "mock/mock_time_get.h"
|
||||
#include "mock/mock_rest_api.h"
|
||||
#include "mock/mock_tenant_manager.h"
|
||||
#include "mock/mock_messaging_downloader.h"
|
||||
#include "config.h"
|
||||
#include "config_component.h"
|
||||
#include "agent_details.h"
|
||||
|
||||
using namespace testing;
|
||||
using namespace std;
|
||||
|
||||
class OrchestrationMultitenancyTest : public Test
|
||||
{
|
||||
public:
|
||||
OrchestrationMultitenancyTest() : config(Singleton::Consume<Config::I_Config>::from(config_comp))
|
||||
{
|
||||
EXPECT_CALL(
|
||||
rest,
|
||||
mockRestCall(RestAction::SET, "new-configuration", _)
|
||||
).WillOnce(WithArg<2>(Invoke(this, &OrchestrationMultitenancyTest::setNewConfiguration)));
|
||||
|
||||
EXPECT_CALL(tenant_manager, getTimeoutVal()).WillOnce(Return(chrono::microseconds(0)));
|
||||
EXPECT_CALL(
|
||||
mock_ml,
|
||||
addRecurringRoutine(I_MainLoop::RoutineType::System, _, _, _, _)
|
||||
).WillRepeatedly(Return(0));
|
||||
EXPECT_CALL(
|
||||
mock_ml,
|
||||
addOneTimeRoutine(I_MainLoop::RoutineType::System, _, "Configuration update registration", false)
|
||||
).WillOnce(Return(0));
|
||||
|
||||
config_comp.preload();
|
||||
config_comp.init();
|
||||
}
|
||||
|
||||
void
|
||||
init()
|
||||
{
|
||||
EXPECT_CALL(mock_service_controller, isServiceInstalled("Access Control")).WillRepeatedly(Return(false));
|
||||
|
||||
// This Holding the Main Routine of the Orchestration.
|
||||
EXPECT_CALL(
|
||||
mock_ml,
|
||||
addOneTimeRoutine(I_MainLoop::RoutineType::RealTime, _, "Orchestration runner", true)
|
||||
).WillOnce(DoAll(SaveArg<1>(&routine), Return(1)));
|
||||
|
||||
EXPECT_CALL(mock_shell_cmd, getExecOutput("openssl version -d | cut -d\" \" -f2 | cut -d\"\\\"\" -f2", _, _))
|
||||
.WillOnce(Return(string("OpenSSL certificates Directory")));
|
||||
|
||||
EXPECT_CALL(rest, mockRestCall(RestAction::SHOW, "orchestration-status", _)).WillOnce(
|
||||
WithArg<2>(Invoke(this, &OrchestrationMultitenancyTest::setRestStatus)));
|
||||
|
||||
doEncrypt();
|
||||
orchestration_comp.init();
|
||||
}
|
||||
|
||||
bool
|
||||
restHandler(const unique_ptr<RestInit> &rest_ptr)
|
||||
{
|
||||
rest_handler = rest_ptr->getRest();
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
doEncrypt()
|
||||
{
|
||||
Maybe<string> err = genError("No file exist");
|
||||
EXPECT_CALL(mock_orchestration_tools, readFile("/etc/cp/conf/user-cred.json")).WillOnce(Return(err));
|
||||
|
||||
EXPECT_CALL(mock_orchestration_tools, writeFile("This is fake", "/etc/cp/data/data1.a")).WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_orchestration_tools, writeFile("0000 is fake", "/etc/cp/data/data4.a")).WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_orchestration_tools, writeFile("This is 3333", "/etc/cp/data/data6.a")).WillOnce(Return(true));
|
||||
}
|
||||
|
||||
void
|
||||
expectDetailsResolver()
|
||||
{
|
||||
Maybe<tuple<string, string, string>> no_nginx(genError("No nginx"));
|
||||
EXPECT_CALL(mock_details_resolver, getPlatform()).WillOnce(Return(string("linux")));
|
||||
EXPECT_CALL(mock_details_resolver, getArch()).WillOnce(Return(string("x86_64")));
|
||||
EXPECT_CALL(mock_details_resolver, isReverseProxy()).WillOnce(Return(false));
|
||||
EXPECT_CALL(mock_details_resolver, isKernelVersion3OrHigher()).WillOnce(Return(false));
|
||||
EXPECT_CALL(mock_details_resolver, isGwNotVsx()).WillOnce(Return(false));
|
||||
EXPECT_CALL(mock_details_resolver, isVersionEqualOrAboveR8110()).WillOnce(Return(false));
|
||||
EXPECT_CALL(mock_details_resolver, parseNginxMetadata()).WillOnce(Return(no_nginx));
|
||||
EXPECT_CALL(mock_details_resolver, getAgentVersion())
|
||||
.WillOnce(Return("1.1.1"))
|
||||
.WillOnce(Return("1.1.1"));
|
||||
|
||||
map<string, string> resolved_mgmt_details({{"kernel_version", "4.4.0-87-generic"}});
|
||||
EXPECT_CALL(mock_details_resolver, getResolvedDetails()).WillOnce(Return(resolved_mgmt_details));
|
||||
}
|
||||
|
||||
void
|
||||
runRoutine()
|
||||
{
|
||||
routine();
|
||||
}
|
||||
|
||||
void
|
||||
preload()
|
||||
{
|
||||
orchestration_comp.preload();
|
||||
}
|
||||
|
||||
void
|
||||
waitForRestCall()
|
||||
{
|
||||
EXPECT_CALL(rest, mockRestCall(RestAction::SHOW, "orchestration-status", _)).WillRepeatedly(Return(true));
|
||||
}
|
||||
|
||||
void
|
||||
performSetNewConfiguration(const string &file_path)
|
||||
{
|
||||
stringstream rest_call_parameters;
|
||||
rest_call_parameters
|
||||
<< "{\"configuration_file_paths\": ["
|
||||
<< (file_path == "" ? file_path : (string("\"") + file_path + string("\"")))
|
||||
<< "] }";
|
||||
set_new_configuration->performRestCall(rest_call_parameters);
|
||||
}
|
||||
|
||||
bool
|
||||
declareVariable(const unique_ptr<RestInit> &p)
|
||||
{
|
||||
set_new_configuration = p->getRest();
|
||||
return true;
|
||||
}
|
||||
|
||||
::Environment env;
|
||||
OrchestrationComp orchestration_comp;
|
||||
AgentDetails agent_details;
|
||||
ConfigComponent config_comp;
|
||||
Config::I_Config *config;
|
||||
|
||||
unique_ptr<ServerRest> set_new_configuration;
|
||||
unique_ptr<ServerRest> rest_status;
|
||||
unique_ptr<ServerRest> rest_handler;
|
||||
unique_ptr<ServerRest> declare_variable;
|
||||
|
||||
StrictMock<MockMainLoop> mock_ml;
|
||||
StrictMock<MockEncryptor> mock_encryptor;
|
||||
StrictMock<MockOrchestrationTools> mock_orchestration_tools;
|
||||
StrictMock<MockDownloader> mock_downloader;
|
||||
StrictMock<MockShellCmd> mock_shell_cmd;
|
||||
StrictMock<MockMessaging> mock_message;
|
||||
StrictMock<MockRestApi> rest;
|
||||
StrictMock<MockServiceController> mock_service_controller;
|
||||
StrictMock<MockManifestController> mock_manifest_controller;
|
||||
StrictMock<MockUpdateCommunication> mock_update_communication;
|
||||
StrictMock<MockMessagingDownloader> mock_messaging_downloader;
|
||||
StrictMock<MockTenantManager> tenant_manager;
|
||||
|
||||
NiceMock<MockOrchestrationStatus> mock_status;
|
||||
NiceMock<MockTimeGet> mock_time_get;
|
||||
NiceMock<MockDetailsResolver> mock_details_resolver;
|
||||
NiceMock<MockAgenetDetailsReporter> mock_agent_reporter;
|
||||
NiceMock<MockLogging> mock_log;
|
||||
|
||||
|
||||
private:
|
||||
bool
|
||||
setNewConfiguration(const unique_ptr<RestInit> &p)
|
||||
{
|
||||
set_new_configuration = p->getRest();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool setRestStatus(const unique_ptr<RestInit> &p)
|
||||
{
|
||||
rest_status = p->getRest();
|
||||
return true;
|
||||
}
|
||||
|
||||
I_MainLoop::Routine routine;
|
||||
I_MainLoop::Routine status_routine;
|
||||
};
|
||||
|
||||
TEST_F(OrchestrationMultitenancyTest, init)
|
||||
{
|
||||
}
|
||||
|
||||
TEST_F(OrchestrationMultitenancyTest, handle_virtual_resource)
|
||||
{
|
||||
string orchestration_policy_file_path = "/etc/cp/conf/orchestration/orchestration.policy";
|
||||
string manifest_file_path = "/etc/cp/conf/manifest.json";
|
||||
string setting_file_path = "/etc/cp/conf/settings.json";
|
||||
string policy_file_path = "/etc/cp/conf/policy.json";
|
||||
string data_file_path = "/etc/cp/conf/data.json";
|
||||
|
||||
string host_address = "1.2.3.5";
|
||||
string manifest_checksum= "manifest";
|
||||
string policy_checksum= "policy";
|
||||
string settings_checksum= "settings";
|
||||
string data_checksum= "data";
|
||||
|
||||
string first_policy_version = "";
|
||||
string host_url = "https://" + host_address + "/";
|
||||
|
||||
EXPECT_CALL(
|
||||
rest,
|
||||
mockRestCall(RestAction::ADD, "proxy", _)
|
||||
).WillOnce(WithArg<2>(Invoke(this, &OrchestrationMultitenancyTest::restHandler)));
|
||||
waitForRestCall();
|
||||
init();
|
||||
expectDetailsResolver();
|
||||
|
||||
Maybe<string> response(
|
||||
string(
|
||||
"{\n"
|
||||
" \"fog-address\": \"" + host_url + "\",\n"
|
||||
" \"agent-type\": \"test\",\n"
|
||||
" \"pulling-interval\": 25,\n"
|
||||
" \"error-pulling-interval\": 15\n"
|
||||
"}"
|
||||
)
|
||||
);
|
||||
|
||||
EXPECT_CALL(mock_orchestration_tools, doesFileExist(orchestration_policy_file_path)).WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_orchestration_tools, readFile(orchestration_policy_file_path)).WillOnce(Return(response));
|
||||
EXPECT_CALL(mock_message, setActiveFog(host_address, 443, true, MessageTypeTag::GENERIC)).WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_update_communication, setAddressExtenesion(""));
|
||||
EXPECT_CALL(mock_update_communication, authenticateAgent()).WillOnce(Return(Maybe<void>()));
|
||||
EXPECT_CALL(mock_manifest_controller, loadAfterSelfUpdate()).WillOnce(Return(false));
|
||||
EXPECT_CALL(mock_orchestration_tools, calculateChecksum(Package::ChecksumTypes::SHA256, manifest_file_path))
|
||||
.WillOnce(Return(manifest_checksum));
|
||||
|
||||
EXPECT_CALL(mock_orchestration_tools, calculateChecksum(Package::ChecksumTypes::SHA256, setting_file_path))
|
||||
.WillOnce(Return(settings_checksum));
|
||||
|
||||
EXPECT_CALL(mock_orchestration_tools, calculateChecksum(Package::ChecksumTypes::SHA256, policy_file_path))
|
||||
.WillOnce(Return(policy_checksum));
|
||||
|
||||
EXPECT_CALL(mock_orchestration_tools, calculateChecksum(Package::ChecksumTypes::SHA256, data_file_path))
|
||||
.WillOnce(Return(data_checksum));
|
||||
|
||||
EXPECT_CALL(mock_service_controller, getPolicyVersion())
|
||||
.Times(2).WillRepeatedly(ReturnRef(first_policy_version));
|
||||
|
||||
vector<string> active_tenants = { "1236", "1235" };
|
||||
EXPECT_CALL(tenant_manager, fetchActiveTenants()).WillOnce(Return(active_tenants));
|
||||
|
||||
EXPECT_CALL(mock_orchestration_tools, calculateChecksum(_, "/etc/cp/conf/tenant_1236/policy.json"))
|
||||
.WillOnce(Return(string("checksum_policy_tenant_1236")));
|
||||
|
||||
EXPECT_CALL(mock_orchestration_tools, calculateChecksum(_, "/etc/cp/conf/tenant_1235/policy.json"))
|
||||
.WillOnce(Return(string("checksum_policy_tenant_1235")));
|
||||
|
||||
EXPECT_CALL(mock_orchestration_tools, readFile("/etc/cp/conf/tenant_1236/policy.json"))
|
||||
.WillOnce(Return(string("{}")));
|
||||
|
||||
EXPECT_CALL(mock_orchestration_tools, readFile("/etc/cp/conf/tenant_1235/policy.json"))
|
||||
.WillOnce(Return(string("{}")));
|
||||
|
||||
|
||||
EXPECT_CALL(mock_orchestration_tools, calculateChecksum(_, "/etc/cp/conf/tenant_1236_settings.json"))
|
||||
.WillOnce(Return(string("checksum_settings_tenant_1236")));
|
||||
|
||||
EXPECT_CALL(mock_orchestration_tools, calculateChecksum(_, "/etc/cp/conf/tenant_1235_settings.json"))
|
||||
.WillOnce(Return(string("checksum_settings_tenant_1235")));
|
||||
|
||||
EXPECT_CALL(mock_update_communication, getUpdate(_)).WillOnce(
|
||||
Invoke(
|
||||
[&](CheckUpdateRequest &req)
|
||||
{
|
||||
EXPECT_THAT(req.getPolicy(), IsValue(policy_checksum));
|
||||
EXPECT_THAT(req.getSettings(), IsValue(settings_checksum));
|
||||
EXPECT_THAT(req.getManifest(), IsValue(manifest_checksum));
|
||||
EXPECT_THAT(req.getData(), IsValue(data_checksum));
|
||||
|
||||
string update_response =
|
||||
"{\n"
|
||||
" \"manifest\": \"\",\n"
|
||||
" \"policy\": \"\",\n"
|
||||
" \"settings\": \"\",\n"
|
||||
" \"data\": \"\",\n"
|
||||
" \"virtualPolicy\": {\n"
|
||||
" \"tenants\": [\n"
|
||||
" {\n"
|
||||
" \"tenantId\": \"1236\",\n"
|
||||
" \"checksum\": \"new_checksum_policy_tenant_1236\",\n"
|
||||
" \"version\": \"1\"\n"
|
||||
" },\n"
|
||||
" {\n"
|
||||
" \"tenantId\": \"1235\",\n"
|
||||
" \"checksum\": \"new_checksum_policy_tenant_1235\",\n"
|
||||
" \"version\": \"1\"\n"
|
||||
" }\n"
|
||||
" ]\n"
|
||||
" },\n"
|
||||
" \"virtualSettings\": {\n"
|
||||
" \"tenants\": [\n"
|
||||
" {\n"
|
||||
" \"tenantId\": \"1236\",\n"
|
||||
" \"checksum\": \"new_checksum_settings_tenant_1236\",\n"
|
||||
" \"version\": \"1\"\n"
|
||||
" },\n"
|
||||
" {\n"
|
||||
" \"tenantId\": \"1235\",\n"
|
||||
" \"checksum\": \"new_checksum_settings_tenant_1235\",\n"
|
||||
" \"version\": \"1\"\n"
|
||||
" }\n"
|
||||
" ]\n"
|
||||
" }\n"
|
||||
"}";
|
||||
|
||||
EXPECT_TRUE(req.loadJson(update_response));
|
||||
|
||||
return Maybe<void>();
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
GetResourceFile policy_file(GetResourceFile::ResourceFileType::VIRTUAL_POLICY);
|
||||
policy_file.addTenant("1236", "1", "new_checksum_policy_tenant_1236");
|
||||
policy_file.addTenant("1235", "1", "new_checksum_policy_tenant_1235");
|
||||
|
||||
map<string, string> download_policy_res = {
|
||||
{ "1236", "/tmp/orchestration_downloads/virtualPolicy_1236.download" },
|
||||
{ "1235", "/tmp/orchestration_downloads/virtualPolicy_1235.download" }
|
||||
};
|
||||
|
||||
GetResourceFile settings_file(GetResourceFile::ResourceFileType::VIRTUAL_SETTINGS);
|
||||
settings_file.addTenant("1236", "1", "new_checksum_settings_tenant_1236");
|
||||
settings_file.addTenant("1235", "1", "new_checksum_settings_tenant_1235");
|
||||
|
||||
map<string, string> download_settings_res = {
|
||||
{ "1236", "/tmp/orchestration_downloads/virtualSettings_1236.download" },
|
||||
{ "1235", "/tmp/orchestration_downloads/virtualSettings_1235.download" }
|
||||
};
|
||||
|
||||
EXPECT_CALL(
|
||||
mock_downloader,
|
||||
downloadVirtualFileFromFog(_, Package::ChecksumTypes::SHA256)
|
||||
).WillOnce(
|
||||
WithArg<0>(
|
||||
Invoke(
|
||||
[&] (const GetResourceFile &resourse_file)
|
||||
{
|
||||
EXPECT_EQ(resourse_file, policy_file);
|
||||
return download_policy_res;
|
||||
}
|
||||
)
|
||||
)
|
||||
).WillOnce(
|
||||
WithArg<0>(
|
||||
Invoke(
|
||||
[&] (const GetResourceFile &resourse_file)
|
||||
{
|
||||
EXPECT_EQ(resourse_file, settings_file);
|
||||
return download_settings_res;
|
||||
}
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
EXPECT_CALL(
|
||||
mock_orchestration_tools,
|
||||
copyFile(
|
||||
"/tmp/orchestration_downloads/virtualSettings_1236.download",
|
||||
"/etc/cp/conf/tenant_1236_settings.json"
|
||||
)
|
||||
).WillOnce(Return(true));
|
||||
|
||||
EXPECT_CALL(
|
||||
mock_orchestration_tools,
|
||||
copyFile(
|
||||
"/tmp/orchestration_downloads/virtualSettings_1235.download",
|
||||
"/etc/cp/conf/tenant_1235_settings.json"
|
||||
)
|
||||
).WillOnce(Return(true));
|
||||
|
||||
vector<string> expected_data_types = {};
|
||||
EXPECT_CALL(
|
||||
mock_service_controller,
|
||||
updateServiceConfiguration(
|
||||
"/etc/cp/conf/policy.json",
|
||||
"/etc/cp/conf/settings.json",
|
||||
expected_data_types,
|
||||
""
|
||||
)
|
||||
).WillOnce(Return(true));
|
||||
|
||||
EXPECT_CALL(
|
||||
mock_service_controller,
|
||||
updateServiceConfiguration(
|
||||
"/tmp/orchestration_downloads/virtualPolicy_1236.download",
|
||||
"/etc/cp/conf/tenant_1236_settings.json",
|
||||
expected_data_types,
|
||||
"1236"
|
||||
)
|
||||
).WillOnce(Return(true));
|
||||
|
||||
EXPECT_CALL(
|
||||
mock_service_controller,
|
||||
updateServiceConfiguration(
|
||||
"/tmp/orchestration_downloads/virtualPolicy_1235.download",
|
||||
"/etc/cp/conf/tenant_1235_settings.json",
|
||||
expected_data_types,
|
||||
"1235"
|
||||
)
|
||||
).WillOnce(Return(true));
|
||||
|
||||
EXPECT_CALL(mock_ml, yield(A<chrono::microseconds>()))
|
||||
.WillOnce(
|
||||
Invoke(
|
||||
[] (chrono::microseconds microseconds)
|
||||
{
|
||||
EXPECT_EQ(1000000, microseconds.count());
|
||||
}
|
||||
)
|
||||
)
|
||||
.WillOnce(
|
||||
Invoke(
|
||||
[] (chrono::microseconds microseconds)
|
||||
{
|
||||
EXPECT_EQ(25000000, microseconds.count());
|
||||
throw invalid_argument("stop while loop");
|
||||
}
|
||||
)
|
||||
);
|
||||
EXPECT_CALL(
|
||||
mock_shell_cmd,
|
||||
getExecOutput(_, _, _)
|
||||
).WillRepeatedly(Return(string("daniel\n1\n")));
|
||||
try {
|
||||
runRoutine();
|
||||
} catch (const invalid_argument& e) {}
|
||||
}
|
||||
1705
components/security_apps/orchestration/orchestration_ut/orchestration_ut.cc
Executable file
1705
components/security_apps/orchestration/orchestration_ut/orchestration_ut.cc
Executable file
File diff suppressed because it is too large
Load Diff
3
components/security_apps/orchestration/package_handler/CMakeLists.txt
Executable file
3
components/security_apps/orchestration/package_handler/CMakeLists.txt
Executable file
@@ -0,0 +1,3 @@
|
||||
add_library(package_handler package_handler.cc)
|
||||
|
||||
add_subdirectory(package_handler_ut)
|
||||
508
components/security_apps/orchestration/package_handler/package_handler.cc
Executable file
508
components/security_apps/orchestration/package_handler/package_handler.cc
Executable file
@@ -0,0 +1,508 @@
|
||||
// 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 "package_handler.h"
|
||||
#include "config.h"
|
||||
#include "sasal.h"
|
||||
#include "i_shell_cmd.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <vector>
|
||||
|
||||
SASAL_START // Orchestration - Updates Control
|
||||
|
||||
USE_DEBUG_FLAG(D_ORCHESTRATOR);
|
||||
|
||||
using namespace std;
|
||||
|
||||
#ifdef smb
|
||||
static const string InstallEnvPrefix = "TMPDIR=/storage/tmp ";
|
||||
#else
|
||||
static const string InstallEnvPrefix = "";
|
||||
#endif
|
||||
|
||||
enum class PackageHandlerActions {
|
||||
INSTALL,
|
||||
UNINSTALL,
|
||||
PREINSTALL,
|
||||
POSTINSTALL,
|
||||
UNREGISTER,
|
||||
GET_VERSION
|
||||
};
|
||||
|
||||
class AdditionalFlagsConfiguration
|
||||
{
|
||||
public:
|
||||
AdditionalFlagsConfiguration() : flags() {}
|
||||
|
||||
void
|
||||
load(cereal::JSONInputArchive &ar)
|
||||
{
|
||||
try {
|
||||
ar(cereal::make_nvp("flags", flags));
|
||||
} catch (cereal::Exception &) {
|
||||
ar.setNextName(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
const vector<string> & getFlags() const { return flags; }
|
||||
|
||||
private:
|
||||
vector<string> flags;
|
||||
};
|
||||
|
||||
class PackageHandler::Impl : Singleton::Provide<I_PackageHandler>::From<PackageHandler>
|
||||
{
|
||||
public:
|
||||
void
|
||||
init()
|
||||
{
|
||||
filesystem_prefix = getFilesystemPathConfig();
|
||||
dbgTrace(D_ORCHESTRATOR) << "Initializing Packet handler, file system path prefix: " << filesystem_prefix;
|
||||
}
|
||||
bool shouldInstallPackage(const string &package_name, const string &install_file_path) const override;
|
||||
|
||||
bool installPackage(const string &package_name, const string &install_file_path, bool restore_mode) const override;
|
||||
|
||||
bool
|
||||
uninstallPackage(
|
||||
const string &package_name,
|
||||
const string &package_path,
|
||||
const string &install_file_path
|
||||
) const override;
|
||||
|
||||
bool preInstallPackage(const string &package_name, const string &install_file_path) const override;
|
||||
|
||||
bool postInstallPackage(const string &package_name, const string &install_file_path) const override;
|
||||
|
||||
bool updateSavedPackage(const string &package_name, const string &install_file_path) const override;
|
||||
|
||||
private:
|
||||
void
|
||||
revertPackage(
|
||||
const string &package_name,
|
||||
bool restore_mode,
|
||||
const string ¤t_installation_file,
|
||||
const string &backup_installation_file
|
||||
) const;
|
||||
|
||||
bool setExecutionMode(const string &install_file_path) const;
|
||||
|
||||
string filesystem_prefix;
|
||||
};
|
||||
|
||||
static string
|
||||
packageHandlerActionsToString(PackageHandlerActions action)
|
||||
{
|
||||
switch(action) {
|
||||
case PackageHandlerActions::INSTALL: {
|
||||
string installation_mode = " --install";
|
||||
auto trusted_ca_directory = getConfiguration<string>("message", "Trusted CA directory");
|
||||
if (trusted_ca_directory.ok() && !trusted_ca_directory.unpack().empty()) {
|
||||
installation_mode += " --certs-dir ";
|
||||
installation_mode += trusted_ca_directory.unpack();
|
||||
}
|
||||
AdditionalFlagsConfiguration additional_flags = getConfigurationWithDefault<AdditionalFlagsConfiguration>(
|
||||
AdditionalFlagsConfiguration(),
|
||||
"orchestration",
|
||||
"additional flags"
|
||||
);
|
||||
for (const auto &flag : additional_flags.getFlags()) {
|
||||
installation_mode += " " + flag;
|
||||
}
|
||||
|
||||
return installation_mode;
|
||||
}
|
||||
case PackageHandlerActions::UNINSTALL: {
|
||||
return string(" --uninstall");
|
||||
}
|
||||
case PackageHandlerActions::PREINSTALL: {
|
||||
return string(" --pre_install_test");
|
||||
}
|
||||
case PackageHandlerActions::POSTINSTALL: {
|
||||
return string(" --post_install_test");
|
||||
}
|
||||
case PackageHandlerActions::UNREGISTER: {
|
||||
return string(" --un-register ");
|
||||
}
|
||||
case PackageHandlerActions::GET_VERSION: {
|
||||
return string(" --version");
|
||||
}
|
||||
}
|
||||
|
||||
dbgAssert(false) << "Package handler action is not supported. Action: " << static_cast<unsigned int>(action);
|
||||
return string();
|
||||
}
|
||||
|
||||
void
|
||||
PackageHandler::init()
|
||||
{
|
||||
pimpl->init();
|
||||
}
|
||||
|
||||
void
|
||||
PackageHandler::preload()
|
||||
{
|
||||
registerExpectedConfiguration<bool>("orchestration", "Debug mode");
|
||||
registerExpectedConfiguration<AdditionalFlagsConfiguration>("orchestration", "additional flags");
|
||||
registerExpectedConfiguration<uint>("orchestration", "Shell command execution time out");
|
||||
}
|
||||
|
||||
bool
|
||||
PackageHandler::Impl::setExecutionMode(const string &install_file_path) const
|
||||
{
|
||||
return (chmod(install_file_path.c_str(), S_IRUSR | S_IWUSR | S_IXUSR) == 0);
|
||||
}
|
||||
|
||||
bool
|
||||
PackageHandler::Impl::shouldInstallPackage(const string &package_name, const string &install_file_path) const
|
||||
{
|
||||
string packages_dir = getConfigurationWithDefault<string>(
|
||||
filesystem_prefix + "/packages",
|
||||
"orchestration",
|
||||
"Packages directory"
|
||||
);
|
||||
|
||||
I_OrchestrationTools *orchestration_tools = Singleton::Consume<I_OrchestrationTools>::by<PackageHandler>();
|
||||
string current_installation_file = packages_dir + "/" + package_name + "/" + package_name;
|
||||
if (!orchestration_tools->doesFileExist(current_installation_file)) {
|
||||
dbgDebug(D_ORCHESTRATOR) << "Clean installation - package should be installed. Package name: " << package_name;
|
||||
return true;
|
||||
}
|
||||
|
||||
setExecutionMode(current_installation_file);
|
||||
setExecutionMode(install_file_path);
|
||||
|
||||
dbgDebug(D_ORCHESTRATOR) << "Checking if new and current packages has different versions";
|
||||
|
||||
uint timeout = getConfigurationWithDefault<uint>(5000, "orchestration", "Shell command execution time out");
|
||||
static const string action = packageHandlerActionsToString(PackageHandlerActions::GET_VERSION);
|
||||
|
||||
I_ShellCmd *shell_cmd = Singleton::Consume<I_ShellCmd>::by<PackageHandler>();
|
||||
Maybe<string> current_package_version = shell_cmd->getExecOutput(current_installation_file + action, timeout);
|
||||
Maybe<string> new_package_version = shell_cmd->getExecOutput(install_file_path + action, timeout);
|
||||
|
||||
if (!current_package_version.ok()) {
|
||||
dbgWarning(D_ORCHESTRATOR)
|
||||
<< "Failed to get version of current package - Upgrade will be executed. Package name: "
|
||||
<< package_name
|
||||
<< ", Error: "
|
||||
<< current_package_version.getErr();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!new_package_version.ok()) {
|
||||
dbgWarning(D_ORCHESTRATOR)
|
||||
<< "Failed to get version of new package - Upgrade will be executed. Package name: "
|
||||
<< package_name
|
||||
<< ", Error: "
|
||||
<< new_package_version.getErr();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool should_install = current_package_version.unpack() != new_package_version.unpack();
|
||||
|
||||
dbgInfo(D_ORCHESTRATOR)
|
||||
<< "Version for both new and current version successfully extracted. Package name: "
|
||||
<< package_name
|
||||
<< ", Current version: "
|
||||
<< current_package_version.unpack()
|
||||
<< ", New version: "
|
||||
<< new_package_version.unpack()
|
||||
<< ", Should install: "
|
||||
<< (should_install ? "yes" : "no");
|
||||
|
||||
return should_install;
|
||||
}
|
||||
|
||||
bool
|
||||
PackageHandler::Impl::installPackage(
|
||||
const string &package_name,
|
||||
const string &install_file_path,
|
||||
bool restore_mode = false) const
|
||||
{
|
||||
I_OrchestrationTools *orchestration_tools = Singleton::Consume<I_OrchestrationTools>::by<PackageHandler>();
|
||||
if (!orchestration_tools->doesFileExist(install_file_path)) {
|
||||
dbgWarning(D_ORCHESTRATOR)
|
||||
<< "Installation file is not valid for update. File path: "
|
||||
<< install_file_path
|
||||
<< " , package: "
|
||||
<< package_name;
|
||||
return false;
|
||||
}
|
||||
|
||||
string packages_dir = getConfigurationWithDefault<string>(
|
||||
filesystem_prefix + "/packages",
|
||||
"orchestration",
|
||||
"Packages directory"
|
||||
);
|
||||
string backup_extension = getConfigurationWithDefault<string>(".bk", "orchestration", "Backup file extension");
|
||||
string current_installation_file = packages_dir + "/" + package_name + "/" + package_name;
|
||||
string backup_installation_file = current_installation_file + backup_extension;
|
||||
|
||||
if (restore_mode) {
|
||||
dbgDebug(D_ORCHESTRATOR) << "Installing package: " << package_name << " from backup.";
|
||||
} else {
|
||||
dbgDebug(D_ORCHESTRATOR) << "Installing package: " << package_name;
|
||||
}
|
||||
|
||||
dbgDebug(D_ORCHESTRATOR) << "Changing permissions to execute installation file " << install_file_path;
|
||||
if (!setExecutionMode(install_file_path)) {
|
||||
dbgWarning(D_ORCHESTRATOR) << "Failed to change permission for the installation file of " << package_name;
|
||||
return false;
|
||||
}
|
||||
|
||||
dbgDebug(D_ORCHESTRATOR) << "Start running installation file. Package: "
|
||||
<< package_name
|
||||
<< ", path: "
|
||||
<< install_file_path;
|
||||
auto action = packageHandlerActionsToString(PackageHandlerActions::INSTALL);
|
||||
bool cmd_result = orchestration_tools->executeCmd(InstallEnvPrefix + install_file_path + action);
|
||||
if (!cmd_result) {
|
||||
dbgWarning(D_ORCHESTRATOR) << "Failed installing package: " << package_name;
|
||||
revertPackage(package_name, restore_mode, current_installation_file, backup_installation_file);
|
||||
return false;
|
||||
}
|
||||
|
||||
// In restore mode, we should exit to prevent infinite loop
|
||||
if (restore_mode) return true;
|
||||
|
||||
if (
|
||||
!orchestration_tools->doesFileExist(current_installation_file) &&
|
||||
!orchestration_tools->copyFile(install_file_path, current_installation_file)
|
||||
) {
|
||||
dbgWarning(D_ORCHESTRATOR)
|
||||
<< "Failed to save installation file. File: "
|
||||
<< install_file_path
|
||||
<< ". Target path: "
|
||||
<< current_installation_file;
|
||||
return false;
|
||||
}
|
||||
|
||||
dbgDebug(D_ORCHESTRATOR) << "Backup installation file to " << backup_installation_file;
|
||||
if (!orchestration_tools->copyFile(current_installation_file, backup_installation_file)) {
|
||||
dbgWarning(D_ORCHESTRATOR) << "Failed to backup installation file: " << current_installation_file;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
PackageHandler::Impl::revertPackage(
|
||||
const string &package_name,
|
||||
bool restore_mode,
|
||||
const string ¤t_installation_file,
|
||||
const string &backup_installation_file) const
|
||||
{
|
||||
string orch_service_name = getConfigurationWithDefault<string>(
|
||||
"orchestration",
|
||||
"orchestration",
|
||||
"Service name"
|
||||
);
|
||||
string packages_dir = getConfigurationWithDefault<string>(
|
||||
filesystem_prefix + "/packages",
|
||||
"orchestration",
|
||||
"Packages directory"
|
||||
);
|
||||
if (package_name == orch_service_name) {
|
||||
string manifest_file_path = getConfigurationWithDefault<string>(
|
||||
filesystem_prefix + "/conf/manifest.json",
|
||||
"orchestration",
|
||||
"Manifest file path"
|
||||
);
|
||||
string temp_extension = getConfigurationWithDefault<string>("_temp", "orchestration", "Temp file extension");
|
||||
string temp_manifest_file(manifest_file_path + temp_extension);
|
||||
|
||||
I_OrchestrationTools *orchestration_tools = Singleton::Consume<I_OrchestrationTools>::by<PackageHandler>();
|
||||
orchestration_tools->removeFile(temp_manifest_file);
|
||||
}
|
||||
|
||||
if (restore_mode) return;
|
||||
|
||||
// First we try to recover to last running package and then to
|
||||
// the backup (2 recent versions are kept)
|
||||
if (!installPackage(package_name, current_installation_file, true)) {
|
||||
dbgWarning(D_ORCHESTRATOR)
|
||||
<< "Failed to recover from current installation package,"
|
||||
<< " trying to use backup package. Current package: "
|
||||
<< current_installation_file;
|
||||
if (!installPackage(package_name, backup_installation_file, true)) {
|
||||
dbgWarning(D_ORCHESTRATOR)
|
||||
<< "Failed to recover from backup installation package. Backup package: "
|
||||
<< backup_installation_file;
|
||||
} else {
|
||||
dbgInfo(D_ORCHESTRATOR)
|
||||
<< "Installation of the backup package succeeded. Backup package: "
|
||||
<< backup_installation_file;
|
||||
}
|
||||
} else {
|
||||
dbgInfo(D_ORCHESTRATOR)
|
||||
<< "Installation of the latest package succeeded. Current package: "
|
||||
<< current_installation_file;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
PackageHandler::Impl::uninstallPackage(
|
||||
const string &package_name,
|
||||
const string &package_path,
|
||||
const string &install_file_path) const
|
||||
{
|
||||
I_OrchestrationTools *orchestration_tools = Singleton::Consume<I_OrchestrationTools>::by<PackageHandler>();
|
||||
if (!orchestration_tools->doesFileExist(install_file_path)) {
|
||||
dbgWarning(D_ORCHESTRATOR) << "Installation file does not exist. File: " << install_file_path;
|
||||
return false;
|
||||
}
|
||||
|
||||
string watchdog_path = getConfigurationWithDefault<string>(
|
||||
filesystem_prefix,
|
||||
"orchestration",
|
||||
"Default Check Point directory"
|
||||
) + "/watchdog/cp-nano-watchdog";
|
||||
auto action = packageHandlerActionsToString(PackageHandlerActions::UNREGISTER);
|
||||
if (!orchestration_tools->executeCmd(InstallEnvPrefix + watchdog_path + action + package_path)) {
|
||||
dbgWarning(D_ORCHESTRATOR) << "Failed to unregister package from watchdog. Package: " << package_name;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!setExecutionMode(install_file_path)) {
|
||||
dbgWarning(D_ORCHESTRATOR) << "Failed to change package permission. Package: " << package_name;
|
||||
return false;
|
||||
}
|
||||
|
||||
action = packageHandlerActionsToString(PackageHandlerActions::UNINSTALL);
|
||||
if (!orchestration_tools->executeCmd(InstallEnvPrefix + install_file_path + action)) {
|
||||
dbgWarning(D_ORCHESTRATOR) << "Failed to uninstall package. Package: " << package_name;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!orchestration_tools->removeFile(install_file_path)) {
|
||||
dbgWarning(D_ORCHESTRATOR) << "Failed to remove installation package files. Package: " << package_name;
|
||||
}
|
||||
|
||||
string backup_ext = getConfigurationWithDefault<string>(
|
||||
".bk",
|
||||
"orchestration",
|
||||
"Backup file extension"
|
||||
);
|
||||
|
||||
if (!orchestration_tools->removeFile(install_file_path + backup_ext)) {
|
||||
dbgDebug(D_ORCHESTRATOR) << "Failed to remove backup installation package files. Package: " << package_name;
|
||||
}
|
||||
|
||||
dbgInfo(D_ORCHESTRATOR) << "Package was uninstalled successfully. Package: " << package_name;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
PackageHandler::Impl::preInstallPackage(const string &package_name, const string &install_file_path) const
|
||||
{
|
||||
I_OrchestrationTools *orchestration_tools = Singleton::Consume<I_OrchestrationTools>::by<PackageHandler>();
|
||||
if (!orchestration_tools->doesFileExist(install_file_path)) {
|
||||
dbgWarning(D_ORCHESTRATOR) << "Installation file does not exist. File: " << install_file_path;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!setExecutionMode(install_file_path)) {
|
||||
dbgWarning(D_ORCHESTRATOR) << "Failed to change package permission. Package: " << package_name;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto action = packageHandlerActionsToString(PackageHandlerActions::PREINSTALL);
|
||||
auto cmd_result = orchestration_tools->executeCmd(InstallEnvPrefix + install_file_path + action);
|
||||
if (!cmd_result) {
|
||||
dbgWarning(D_ORCHESTRATOR) << "Failed during pre installation test. Package: " << package_name;
|
||||
return false;
|
||||
}
|
||||
|
||||
dbgInfo(D_ORCHESTRATOR) << "Pre installation test passed successfully. Package: " << package_name;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
PackageHandler::Impl::postInstallPackage(const string &package_name, const string &install_file_path) const
|
||||
{
|
||||
I_OrchestrationTools *orchestration_tools = Singleton::Consume<I_OrchestrationTools>::by<PackageHandler>();
|
||||
if (!orchestration_tools->doesFileExist(install_file_path)) {
|
||||
dbgWarning(D_ORCHESTRATOR) << "Installation file does not exist. File: " << install_file_path;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!setExecutionMode(install_file_path)) {
|
||||
dbgWarning(D_ORCHESTRATOR) << "Failed to change package permission. Package: " << package_name;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto action = packageHandlerActionsToString(PackageHandlerActions::POSTINSTALL);
|
||||
auto cmd_result = orchestration_tools->executeCmd(InstallEnvPrefix + install_file_path + action);
|
||||
if (!cmd_result) {
|
||||
dbgWarning(D_ORCHESTRATOR) << "Failed during post installation test. Package: " << package_name;
|
||||
string backup_extension = getConfigurationWithDefault<string>(".bk", "orchestration", "Backup file extension");
|
||||
string packages_dir = getConfigurationWithDefault<string>(
|
||||
filesystem_prefix + "/packages",
|
||||
"orchestration",
|
||||
"Packages directory"
|
||||
);
|
||||
string current_installation_file = packages_dir + "/" + package_name + "/" + package_name;
|
||||
revertPackage(package_name, false, current_installation_file, current_installation_file + backup_extension);
|
||||
return false;
|
||||
}
|
||||
dbgInfo(D_ORCHESTRATOR) << "Post installation test passed successfully. Package: " << package_name;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
PackageHandler::Impl::updateSavedPackage(const string &package_name, const string &install_file_path) const
|
||||
{
|
||||
string packages_dir = getConfigurationWithDefault<string>(
|
||||
filesystem_prefix + "/packages",
|
||||
"orchestration",
|
||||
"Packages directory"
|
||||
);
|
||||
string backup_extension = getConfigurationWithDefault<string>(".bk", "orchestration", "Backup file extension");
|
||||
string temp_extension = getConfigurationWithDefault<string>("_temp", "orchestration", "Temp file extension");
|
||||
string current_installation_file = packages_dir + "/" + package_name + "/" + package_name;
|
||||
string current_installation_file_backup = current_installation_file + backup_extension;
|
||||
string tmp_backup = current_installation_file_backup + temp_extension;
|
||||
|
||||
I_OrchestrationTools *orchestration_tools = Singleton::Consume<I_OrchestrationTools>::by<PackageHandler>();
|
||||
// Step 1 - save current installation file backup to temporary file.
|
||||
orchestration_tools->copyFile(current_installation_file_backup, tmp_backup);
|
||||
// Step 2 - save current installation file to the backuop file.
|
||||
orchestration_tools->copyFile(current_installation_file, current_installation_file_backup);
|
||||
dbgDebug(D_ORCHESTRATOR) << "Saving the installation file. "
|
||||
<< "From: " << install_file_path << ", "
|
||||
<< " To: " << current_installation_file;
|
||||
// Step 3 - save the new installation file to the saved package.
|
||||
if (!orchestration_tools->copyFile(install_file_path, current_installation_file)) {
|
||||
dbgWarning(D_ORCHESTRATOR) << "Failed to save installation file. File: " << install_file_path;
|
||||
// Step 3.1 - Revet the backup package
|
||||
orchestration_tools->copyFile(tmp_backup, current_installation_file_backup);
|
||||
return false;
|
||||
}
|
||||
// Step 4 - remove the current package file
|
||||
if (!orchestration_tools->removeFile(install_file_path)) {
|
||||
dbgWarning(D_ORCHESTRATOR) << "Failed to remove temporary installation file. File: " << install_file_path;
|
||||
}
|
||||
// Step 5 - remove the temporary backup file
|
||||
orchestration_tools->removeFile(tmp_backup);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
PackageHandler::PackageHandler() : Component("PackageHandler"), pimpl(make_unique<Impl>()) {}
|
||||
|
||||
PackageHandler::~PackageHandler() {}
|
||||
|
||||
SASAL_END
|
||||
@@ -0,0 +1,8 @@
|
||||
link_directories(${ng_module_osrc_openssl_path}/lib)
|
||||
link_directories(${BOOST_ROOT}/lib)
|
||||
|
||||
add_unit_test(
|
||||
package_handler_ut
|
||||
"package_handler_ut.cc"
|
||||
"package_handler;orchestration_tools;orchestration_modules;singleton;logging;config;metric;event_is;-lcrypto;-lboost_filesystem;-lboost_regex"
|
||||
)
|
||||
@@ -0,0 +1,404 @@
|
||||
#include "package_handler.h"
|
||||
|
||||
#include "cptest.h"
|
||||
#include "config.h"
|
||||
#include "config_component.h"
|
||||
#include "mock/mock_orchestration_tools.h"
|
||||
#include "mock/mock_mainloop.h"
|
||||
#include "mock/mock_time_get.h"
|
||||
#include "mock/mock_shell_cmd.h"
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace testing;
|
||||
|
||||
class PackageHandlerTest : public Test
|
||||
{
|
||||
public:
|
||||
PackageHandlerTest()
|
||||
:
|
||||
package_dir("/tmp/packages"),
|
||||
backup_ext(".bk")
|
||||
{
|
||||
setConfiguration<string>(package_dir, "orchestration", "Packages directory");
|
||||
setConfiguration<string>(backup_ext, "orchestration", "Backup file extension");
|
||||
setConfiguration<string>("/tmp", "orchestration", "Default Check Point directory");
|
||||
|
||||
writeFile("#!/bin/bash\necho \"bb\"\nexit 1", "/tmp/bad.sh");
|
||||
writeFile("#!/bin/bash\necho \"bb\"", "/tmp/packages/good/good");
|
||||
writeFile("#!/bin/bash\necho \"bb\"", "/tmp/good.sh");
|
||||
writeFile("#!/bin/bash\necho \"bb\"", "/tmp/packages/a/a");
|
||||
package_handler.init();
|
||||
}
|
||||
|
||||
~PackageHandlerTest()
|
||||
{
|
||||
namespace fs = boost::filesystem;
|
||||
fs::path path_to_clean(package_dir);
|
||||
if (fs::is_directory(path_to_clean)) {
|
||||
for (fs::directory_iterator iter(path_to_clean); iter != fs::directory_iterator(); ++iter) {
|
||||
fs::remove_all(iter->path());
|
||||
}
|
||||
fs::remove_all(package_dir);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
preload()
|
||||
{
|
||||
package_handler.preload();
|
||||
}
|
||||
|
||||
bool
|
||||
writeFile(const string &text, const string &path) const
|
||||
{
|
||||
if (path.find('/') != string::npos) {
|
||||
try {
|
||||
string dir_path = path.substr(0, path.find_last_of('/'));
|
||||
boost::filesystem::create_directories(dir_path);
|
||||
} catch (const boost::filesystem::filesystem_error& e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
try {
|
||||
ofstream fout(path);
|
||||
fout << text;
|
||||
return true;
|
||||
} catch (const boost::filesystem::filesystem_error& e) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
string package_dir;
|
||||
string backup_ext;
|
||||
::Environment env;
|
||||
ConfigComponent config;
|
||||
NiceMock<MockOrchestrationTools> mock_orchestration_tools;
|
||||
PackageHandler package_handler;
|
||||
I_PackageHandler *i_package_handler = Singleton::Consume<I_PackageHandler>::from(package_handler);
|
||||
NiceMock<MockMainLoop> mock_mainloop;
|
||||
NiceMock<MockTimeGet> mock_timer;
|
||||
StrictMock<MockShellCmd> mock_shell;
|
||||
};
|
||||
|
||||
TEST_F(PackageHandlerTest, doNothing)
|
||||
{
|
||||
}
|
||||
|
||||
TEST_F(PackageHandlerTest, registerExpectedConfig)
|
||||
{
|
||||
env.preload();
|
||||
env.init();
|
||||
|
||||
preload();
|
||||
string config_json =
|
||||
"{\n"
|
||||
" \"orchestration\": {\n"
|
||||
" \"Debug mode\": [\n"
|
||||
" {\n"
|
||||
" \"value\": true\n"
|
||||
" }\n"
|
||||
" ]\n"
|
||||
" }\n"
|
||||
"}";
|
||||
|
||||
istringstream string_stream(config_json);
|
||||
Singleton::Consume<Config::I_Config>::from(config)->loadConfiguration(string_stream);
|
||||
EXPECT_THAT(getConfiguration<bool>("orchestration", "Debug mode"), IsValue(true));
|
||||
env.fini();
|
||||
}
|
||||
|
||||
TEST_F(PackageHandlerTest, useAdditionalFlags)
|
||||
{
|
||||
env.preload();
|
||||
env.init();
|
||||
preload();
|
||||
registerExpectedConfiguration<string>("orchestration", "Packages directory");
|
||||
registerExpectedConfiguration<string>("orchestration", "Backup file extension");
|
||||
registerExpectedConfiguration<string>("orchestration", "Default Check Point directory");
|
||||
|
||||
string config_json =
|
||||
"{\n"
|
||||
" \"orchestration\": {\n"
|
||||
" \"additional flags\": [\n"
|
||||
" {\n"
|
||||
" \"flags\": [\n"
|
||||
" \"--flag1\",\n"
|
||||
" \"--flag2\"\n"
|
||||
" ]\n"
|
||||
" }\n"
|
||||
" ],\n"
|
||||
" \"Packages directory\": [ { \"value\": \"" + package_dir + "\"}],\n"
|
||||
" \"Backup file extension\": [ { \"value\": \"" + backup_ext + "\"}],\n"
|
||||
" \"Default Check Point directory\": [ { \"value\": \"/tmp\"}]"
|
||||
" }\n"
|
||||
"}";
|
||||
istringstream string_stream(config_json);
|
||||
Singleton::Consume<Config::I_Config>::from(config)->loadConfiguration(string_stream);
|
||||
|
||||
string script_path = "/tmp/good.sh";
|
||||
EXPECT_CALL(mock_orchestration_tools, doesFileExist(script_path)).WillOnce(Return(true));
|
||||
string package_file = package_dir + "/a/a";
|
||||
EXPECT_CALL(mock_orchestration_tools, doesFileExist(package_file)).WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_orchestration_tools, copyFile(package_file, package_file + backup_ext)).WillOnce(Return(true));
|
||||
|
||||
string install_command = script_path + " --install --flag1 --flag2";
|
||||
EXPECT_CALL(mock_orchestration_tools, executeCmd(install_command)).WillOnce(Return(true));
|
||||
EXPECT_TRUE(i_package_handler->installPackage("a", script_path, false));
|
||||
|
||||
env.fini();
|
||||
}
|
||||
|
||||
TEST_F(PackageHandlerTest, fileNotExist)
|
||||
{
|
||||
EXPECT_CALL(mock_orchestration_tools, doesFileExist("test.json")).WillOnce(Return(false));
|
||||
EXPECT_NE(true, i_package_handler->installPackage("", "test.json", false));
|
||||
}
|
||||
|
||||
TEST_F(PackageHandlerTest, goodInstall)
|
||||
{
|
||||
string script_path = "/tmp/good.sh";
|
||||
EXPECT_CALL(mock_orchestration_tools, doesFileExist(script_path)).WillOnce(Return(true));
|
||||
string package_file = package_dir + "/a/a";
|
||||
EXPECT_CALL(mock_orchestration_tools, doesFileExist(package_file)).WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_orchestration_tools, copyFile(package_file, package_file + backup_ext)).WillOnce(Return(true));
|
||||
|
||||
string command = script_path + " --install";
|
||||
EXPECT_CALL(mock_orchestration_tools, executeCmd(command)).WillOnce(Return(true));
|
||||
EXPECT_TRUE(i_package_handler->installPackage("a", script_path, false));
|
||||
}
|
||||
|
||||
TEST_F(PackageHandlerTest, badInstall)
|
||||
{
|
||||
string package_name = "a";
|
||||
string package_file = package_dir + "/" + package_name + "/" + package_name;
|
||||
string script_path = "/tmp/bad.sh";
|
||||
EXPECT_CALL(mock_orchestration_tools, doesFileExist(script_path)).WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_orchestration_tools, doesFileExist(package_file)).WillOnce(Return(false));
|
||||
EXPECT_CALL(mock_orchestration_tools, doesFileExist(package_file + backup_ext)).WillOnce(Return(false));
|
||||
string command = script_path + " --install";
|
||||
EXPECT_CALL(mock_orchestration_tools, executeCmd(command)).WillOnce(Return(false));
|
||||
EXPECT_FALSE(i_package_handler->installPackage(package_name, script_path, false));
|
||||
}
|
||||
|
||||
TEST_F(PackageHandlerTest, orcInstallErrorWhileCopyCurrent)
|
||||
{
|
||||
string script_path = "/tmp/good.sh";
|
||||
EXPECT_CALL(mock_orchestration_tools, doesFileExist(script_path)).WillOnce(Return(true));
|
||||
string package_file = package_dir + "/a/a";
|
||||
EXPECT_CALL(mock_orchestration_tools, doesFileExist(package_file)).WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_orchestration_tools, copyFile(package_file, package_file + backup_ext)).WillOnce(Return(false));
|
||||
|
||||
string command = script_path + " --install";
|
||||
EXPECT_CALL(mock_orchestration_tools, executeCmd(command)).WillOnce(Return(true));
|
||||
EXPECT_FALSE(i_package_handler->installPackage("a", script_path, false));
|
||||
}
|
||||
|
||||
TEST_F(PackageHandlerTest, orcInstallErrorWhileRemovingNew)
|
||||
{
|
||||
string script_path = "/tmp/good.sh";
|
||||
EXPECT_CALL(mock_orchestration_tools, doesFileExist(script_path)).WillOnce(Return(true));
|
||||
string package_file = package_dir + "/a/a";
|
||||
EXPECT_CALL(mock_orchestration_tools, doesFileExist(package_file)).WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_orchestration_tools, copyFile(package_file, package_file + backup_ext)).WillOnce(Return(true));
|
||||
|
||||
string command = script_path + " --install";
|
||||
EXPECT_CALL(mock_orchestration_tools, executeCmd(command)).WillOnce(Return(true));
|
||||
EXPECT_TRUE(i_package_handler->installPackage("a", script_path, false));
|
||||
}
|
||||
TEST_F(PackageHandlerTest, badInstallAndRecovery)
|
||||
{
|
||||
string package_name = "a";
|
||||
string package_file = package_dir + "/" + package_name + "/" + package_name;
|
||||
string script_path = "/tmp/bad.sh";
|
||||
EXPECT_CALL(mock_orchestration_tools, doesFileExist(script_path)).WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_orchestration_tools, doesFileExist(package_file)).WillOnce(Return(true));
|
||||
|
||||
string command = script_path + " --install";
|
||||
EXPECT_CALL(mock_orchestration_tools, executeCmd(command)).WillOnce(Return(false));
|
||||
|
||||
command = package_file + " --install";
|
||||
EXPECT_CALL(mock_orchestration_tools, executeCmd(command)).WillOnce(Return(true));
|
||||
|
||||
EXPECT_FALSE(i_package_handler->installPackage(package_name, script_path, false));
|
||||
}
|
||||
|
||||
TEST_F(PackageHandlerTest, badOrcInstallAndRecoveryWithDefualValuesChange)
|
||||
{
|
||||
setConfiguration<string>("good", "orchestration", "Service name");
|
||||
string manifest_file_path = getConfigurationWithDefault<string>("/etc/cp/conf/manifest.json",
|
||||
"orchestration", "Manifest file path");
|
||||
string temp_ext = getConfigurationWithDefault<string>("_temp", "orchestration", "Temp file extension");
|
||||
string temp_manifest_file = manifest_file_path + temp_ext;
|
||||
string package_file = package_dir + "/good/good";
|
||||
|
||||
EXPECT_CALL(mock_orchestration_tools, doesFileExist("/tmp/bad.sh")).WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_orchestration_tools, doesFileExist(package_file)).WillOnce(Return(true));
|
||||
|
||||
string command = "/tmp/bad.sh --install";
|
||||
EXPECT_CALL(mock_orchestration_tools, executeCmd(command)).WillOnce(Return(false));
|
||||
|
||||
command = package_file + " --install";
|
||||
EXPECT_CALL(mock_orchestration_tools, executeCmd(command)).WillOnce(Return(true));
|
||||
|
||||
EXPECT_FALSE(i_package_handler->installPackage("good", "/tmp/bad.sh", false));
|
||||
}
|
||||
|
||||
TEST_F(PackageHandlerTest, shouldInstall)
|
||||
{
|
||||
string old_script_path = "/tmp/packages/my-script/my-script";
|
||||
EXPECT_CALL(mock_orchestration_tools, doesFileExist(old_script_path)).WillOnce(Return(true));
|
||||
string new_script_path = "/tmp/new-script.sh";
|
||||
string version_command = " --version";
|
||||
EXPECT_CALL(mock_shell, getExecOutput(old_script_path + version_command, 5000, _)).WillOnce(Return(string("a")));
|
||||
EXPECT_CALL(mock_shell, getExecOutput(new_script_path + version_command, 5000, _)).WillOnce(Return(string("b")));
|
||||
|
||||
EXPECT_TRUE(i_package_handler->shouldInstallPackage("my-script", new_script_path));
|
||||
|
||||
EXPECT_CALL(mock_orchestration_tools, doesFileExist(old_script_path)).WillOnce(Return(false));
|
||||
EXPECT_TRUE(i_package_handler->shouldInstallPackage("my-script", new_script_path));
|
||||
|
||||
EXPECT_CALL(mock_orchestration_tools, doesFileExist(old_script_path)).WillOnce(Return(true));
|
||||
EXPECT_CALL(
|
||||
mock_shell,
|
||||
getExecOutput(old_script_path + version_command, 5000, _)
|
||||
).WillOnce(Return(Maybe<string>(genError("Failed"))));
|
||||
EXPECT_CALL(mock_shell, getExecOutput(new_script_path + version_command, 5000, _)).WillOnce(Return(string("a")));
|
||||
EXPECT_TRUE(i_package_handler->shouldInstallPackage("my-script", new_script_path));
|
||||
}
|
||||
|
||||
TEST_F(PackageHandlerTest, shouldNotInstall)
|
||||
{
|
||||
string old_script_path = "/tmp/packages/my-script/my-script";
|
||||
EXPECT_CALL(mock_orchestration_tools, doesFileExist(old_script_path)).WillOnce(Return(true));
|
||||
string version_command = " --version";
|
||||
EXPECT_CALL(mock_shell, getExecOutput(old_script_path + version_command, 5000, _)).WillOnce(Return(string("a")));
|
||||
string new_script_path = "/tmp/new-script.sh";
|
||||
EXPECT_CALL(mock_shell, getExecOutput(new_script_path + version_command, 5000, _)).WillOnce(Return(string("a")));
|
||||
EXPECT_FALSE(i_package_handler->shouldInstallPackage("my-script", new_script_path));
|
||||
}
|
||||
|
||||
TEST_F(PackageHandlerTest, badPreInstall)
|
||||
{
|
||||
string script_path = "/tmp/bad.sh";
|
||||
EXPECT_CALL(mock_orchestration_tools, doesFileExist(script_path)).WillOnce(Return(false));
|
||||
EXPECT_FALSE(i_package_handler->preInstallPackage("a", script_path));
|
||||
|
||||
EXPECT_CALL(mock_orchestration_tools, doesFileExist(script_path)).WillOnce(Return(true));
|
||||
string command = script_path + " --pre_install_test";
|
||||
EXPECT_CALL(mock_orchestration_tools, executeCmd(command)).WillOnce(Return(false));
|
||||
EXPECT_FALSE(i_package_handler->preInstallPackage("a", script_path));
|
||||
}
|
||||
|
||||
TEST_F(PackageHandlerTest, goodPreInstall)
|
||||
{
|
||||
string script_path = "/tmp/good.sh";
|
||||
EXPECT_CALL(mock_orchestration_tools, doesFileExist(script_path)).WillOnce(Return(true));
|
||||
string command = script_path + " --pre_install_test";
|
||||
EXPECT_CALL(mock_orchestration_tools, executeCmd(command)).WillOnce(Return(true));
|
||||
EXPECT_TRUE(i_package_handler->preInstallPackage("a", script_path));
|
||||
}
|
||||
|
||||
TEST_F(PackageHandlerTest, badPostInstall)
|
||||
{
|
||||
string script_path = "/tmp/bad.sh";
|
||||
EXPECT_CALL(mock_orchestration_tools, doesFileExist(script_path)).WillOnce(Return(false));
|
||||
EXPECT_FALSE(i_package_handler->postInstallPackage("a", script_path));
|
||||
|
||||
string package_file = package_dir + "/a/a";
|
||||
string command = script_path + " --post_install_test";
|
||||
EXPECT_CALL(mock_orchestration_tools, doesFileExist(script_path)).WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_orchestration_tools, executeCmd(command)).WillOnce(Return(false));
|
||||
|
||||
EXPECT_CALL(mock_orchestration_tools, doesFileExist(package_file)).WillOnce(Return(true));
|
||||
command = package_file + " --install";
|
||||
EXPECT_CALL(mock_orchestration_tools, executeCmd(command)).WillOnce(Return(true));
|
||||
|
||||
EXPECT_FALSE(i_package_handler->postInstallPackage("a", script_path));
|
||||
}
|
||||
|
||||
TEST_F(PackageHandlerTest, goodPostInstall)
|
||||
{
|
||||
string script_path = "/tmp/good.sh";
|
||||
EXPECT_CALL(mock_orchestration_tools, doesFileExist(script_path)).WillOnce(Return(true));
|
||||
string package_file = package_dir + "/a/a";
|
||||
string command = script_path + " --post_install_test";
|
||||
EXPECT_CALL(mock_orchestration_tools, executeCmd(command)).WillOnce(Return(true));
|
||||
EXPECT_TRUE(i_package_handler->postInstallPackage("a", script_path));
|
||||
}
|
||||
|
||||
TEST_F(PackageHandlerTest, badUninstall)
|
||||
{
|
||||
string script_path = "/tmp/good.sh";
|
||||
string watchdog_dir = "/tmp/watchdog";
|
||||
string watchdog_path = watchdog_dir + "/cp-nano-watchdog";
|
||||
string package_file = package_dir + "/a/a";
|
||||
EXPECT_CALL(mock_orchestration_tools, doesFileExist(script_path)).WillOnce(Return(false));
|
||||
EXPECT_FALSE(i_package_handler->uninstallPackage("a", package_file, script_path));
|
||||
|
||||
string command = watchdog_path + " --un-register " + package_file;
|
||||
EXPECT_CALL(mock_orchestration_tools, doesFileExist(script_path)).WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_orchestration_tools, executeCmd(command)).WillOnce(Return(false));
|
||||
EXPECT_FALSE(i_package_handler->uninstallPackage("a", package_file, script_path));
|
||||
|
||||
EXPECT_CALL(mock_orchestration_tools, doesFileExist(script_path)).WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_orchestration_tools, executeCmd(command)).WillOnce(Return(true));
|
||||
command = script_path + " --uninstall";
|
||||
EXPECT_CALL(mock_orchestration_tools, executeCmd(command)).WillOnce(Return(false));
|
||||
EXPECT_FALSE(i_package_handler->uninstallPackage("a", package_file, script_path));
|
||||
}
|
||||
|
||||
TEST_F(PackageHandlerTest, goodUninstall)
|
||||
{
|
||||
string script_path = "/tmp/good.sh";
|
||||
string watchdog_dir = "/tmp/watchdog";
|
||||
string watchdog_path = watchdog_dir + "/cp-nano-watchdog";
|
||||
string package_file = package_dir + "/a/a";
|
||||
EXPECT_CALL(mock_orchestration_tools, doesFileExist(script_path)).WillOnce(Return(true));
|
||||
|
||||
string command = watchdog_path + " --un-register " + package_file;
|
||||
EXPECT_CALL(mock_orchestration_tools, executeCmd(command)).WillOnce(Return(true));
|
||||
|
||||
command = script_path + " --uninstall";
|
||||
EXPECT_CALL(mock_orchestration_tools, executeCmd(command)).WillOnce(Return(true));
|
||||
EXPECT_TRUE(i_package_handler->uninstallPackage("a", package_file, script_path));
|
||||
}
|
||||
|
||||
TEST_F(PackageHandlerTest, badupdateSavedPackage)
|
||||
{
|
||||
string script_path = "/tmp/good.sh";
|
||||
string package_file = package_dir + "/a/a";
|
||||
string package_file_backup = package_dir + "/a/a.bk";
|
||||
string package_file_backup_temp = package_dir + "/a/a.bk_temp";
|
||||
EXPECT_CALL(mock_orchestration_tools,
|
||||
copyFile(package_file_backup, package_file_backup_temp)).Times(2).WillRepeatedly(Return(false));
|
||||
EXPECT_CALL(mock_orchestration_tools,
|
||||
copyFile(package_file, package_file_backup)).Times(2).WillRepeatedly(Return(false));
|
||||
|
||||
EXPECT_CALL(mock_orchestration_tools, copyFile(script_path, package_file)).WillOnce(Return(false));
|
||||
EXPECT_CALL(mock_orchestration_tools,
|
||||
copyFile(package_file_backup_temp, package_file_backup)).WillOnce(Return(false));
|
||||
EXPECT_FALSE(i_package_handler->updateSavedPackage("a", script_path));
|
||||
|
||||
EXPECT_CALL(mock_orchestration_tools, copyFile(script_path, package_file)).WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_orchestration_tools, removeFile(script_path)).WillOnce(Return(false));
|
||||
EXPECT_CALL(mock_orchestration_tools, removeFile(package_file_backup_temp)).WillOnce(Return(false));
|
||||
EXPECT_TRUE(i_package_handler->updateSavedPackage("a", script_path));
|
||||
}
|
||||
|
||||
TEST_F(PackageHandlerTest, goodupdateSavedPackage)
|
||||
{
|
||||
string script_path = "/tmp/good.sh";
|
||||
string package_file = package_dir + "/a/a";
|
||||
string package_file_backup = package_dir + "/a/a.bk";
|
||||
string package_file_backup_temp = package_dir + "/a/a.bk_temp";
|
||||
EXPECT_CALL(mock_orchestration_tools,
|
||||
copyFile(package_file_backup, package_file_backup_temp)).WillOnce(Return(false));
|
||||
EXPECT_CALL(mock_orchestration_tools, copyFile(package_file, package_file_backup)).WillOnce(Return(false));
|
||||
EXPECT_CALL(mock_orchestration_tools, copyFile(script_path, package_file)).WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_orchestration_tools, removeFile(script_path)).WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_orchestration_tools, removeFile(package_file_backup_temp)).WillOnce(Return(true));
|
||||
|
||||
EXPECT_TRUE(i_package_handler->updateSavedPackage("a", script_path));
|
||||
}
|
||||
3
components/security_apps/orchestration/service_controller/CMakeLists.txt
Executable file
3
components/security_apps/orchestration/service_controller/CMakeLists.txt
Executable file
@@ -0,0 +1,3 @@
|
||||
add_library(service_controller service_controller.cc)
|
||||
|
||||
add_subdirectory(service_controller_ut)
|
||||
937
components/security_apps/orchestration/service_controller/service_controller.cc
Executable file
937
components/security_apps/orchestration/service_controller/service_controller.cc
Executable file
@@ -0,0 +1,937 @@
|
||||
// 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 "service_controller.h"
|
||||
|
||||
#include <cereal/types/unordered_set.hpp>
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "debug.h"
|
||||
#include "sasal.h"
|
||||
#include "rest.h"
|
||||
#include "connkey.h"
|
||||
#include "i_messaging.h"
|
||||
#include "common.h"
|
||||
#include "log_generator.h"
|
||||
#include "i_orchestration_tools.h"
|
||||
#include "customized_cereal_map.h"
|
||||
|
||||
SASAL_START // Orchestration - Updates Control
|
||||
|
||||
using namespace std;
|
||||
using namespace ReportIS;
|
||||
|
||||
USE_DEBUG_FLAG(D_ORCHESTRATOR);
|
||||
|
||||
class SendConfigurations : public ClientRest
|
||||
{
|
||||
public:
|
||||
SendConfigurations(int _id, const string &ver) : id(_id), policy_version(ver) {}
|
||||
|
||||
BOTH_PARAM(int, id);
|
||||
S2C_PARAM(bool, error);
|
||||
S2C_PARAM(bool, finished);
|
||||
S2C_OPTIONAL_PARAM(string, error_message);
|
||||
C2S_PARAM(string, policy_version);
|
||||
};
|
||||
|
||||
class ServiceReconfStatusMonitor : Singleton::Consume<I_ServiceController>, public ServerRest
|
||||
{
|
||||
public:
|
||||
void
|
||||
doCall() override
|
||||
{
|
||||
auto service_controller = Singleton::Consume<I_ServiceController>::by<ServiceReconfStatusMonitor>();
|
||||
if (!finished.get()) {
|
||||
service_controller->updateReconfStatus(id.get(), ReconfStatus::IN_PROGRESS);
|
||||
dbgTrace(D_ORCHESTRATOR)
|
||||
<< "Request for service reconfiguration, with id "
|
||||
<< id.get()
|
||||
<< ", is still in progress.";
|
||||
return;
|
||||
}
|
||||
if (error.get()) {
|
||||
service_controller->updateReconfStatus(id.get(), ReconfStatus::FAILED);
|
||||
dbgError(D_ORCHESTRATOR)
|
||||
<< "Request for service reconfiguration, with id "
|
||||
<< id.get()
|
||||
<< ", failed to complete."
|
||||
<< (error_message.isActive() ? " Error: " + error_message.get() : "");
|
||||
return;
|
||||
}
|
||||
service_controller->updateReconfStatus(id.get(), ReconfStatus::SUCCEEDED);
|
||||
dbgInfo(D_ORCHESTRATOR)
|
||||
<< "Request for service reconfiguration, with id "
|
||||
<< id.get()
|
||||
<< ", successfully accomplished.";
|
||||
return;
|
||||
}
|
||||
|
||||
private:
|
||||
C2S_PARAM(int, id);
|
||||
C2S_PARAM(bool, error);
|
||||
C2S_PARAM(bool, finished);
|
||||
C2S_OPTIONAL_PARAM(string, error_message);
|
||||
};
|
||||
|
||||
bool
|
||||
ServiceDetails::isServiceActive() const
|
||||
{
|
||||
stringstream watchdog_status_cmd;
|
||||
watchdog_status_cmd
|
||||
<< getFilesystemPathConfig()
|
||||
<< "/watchdog/cp-nano-watchdog --status --verbose --service "
|
||||
<< service_name;
|
||||
|
||||
if (!service_id.empty() && service_id != service_name) {
|
||||
string uuid = "";
|
||||
if (service_id.find("_") != string::npos) {
|
||||
string fid = service_id.substr(0, service_id.find("_"));
|
||||
uuid = service_id.substr(service_id.find("_") + 1, service_id.size());
|
||||
watchdog_status_cmd << " --family " << fid << " --id " << uuid;
|
||||
} else {
|
||||
uuid = service_id;
|
||||
watchdog_status_cmd << " --id " << uuid;
|
||||
}
|
||||
}
|
||||
|
||||
dbgDebug(D_ORCHESTRATOR)
|
||||
<< "Executing service status check via watchdog api. Service name: "
|
||||
<< service_name
|
||||
<< ", Watchdog command: "
|
||||
<< watchdog_status_cmd.str();
|
||||
|
||||
I_ShellCmd *shell_cmd = Singleton::Consume<I_ShellCmd>::by<ServiceController>();
|
||||
Maybe<string> service_status = shell_cmd->getExecOutput(watchdog_status_cmd.str());
|
||||
|
||||
if (!service_status.ok()) {
|
||||
dbgWarning(D_ORCHESTRATOR)
|
||||
<< "Changing service status to inactive after failure to its status from watchdog. Service name: "
|
||||
<< service_name;
|
||||
return false;
|
||||
}
|
||||
|
||||
dbgDebug(D_ORCHESTRATOR)
|
||||
<< "Successfully retrieved service status from watchdog. Service name: "
|
||||
<< service_name
|
||||
<< ", Watchdog output: "
|
||||
<< *service_status;
|
||||
|
||||
string status = service_status.unpack();
|
||||
for_each(status.begin(), status.end(), [](char &c) { c = ::tolower(c); });
|
||||
|
||||
bool is_registered = status.find("not-registered") == string::npos && status.find("registered") != string::npos;
|
||||
bool is_running = status.find("not-running") == string::npos && status.find("running") != string::npos;
|
||||
|
||||
dbgInfo(D_ORCHESTRATOR)
|
||||
<< "Successfully set service status. Service name: "
|
||||
<< service_name
|
||||
<< ", Status: "
|
||||
<< ((is_registered && is_running) ? "active" : "inactive");
|
||||
|
||||
return is_registered && is_running;
|
||||
}
|
||||
|
||||
template <typename Archive>
|
||||
void
|
||||
ServiceDetails::serialize(Archive &ar)
|
||||
{
|
||||
ar(cereal::make_nvp("Service name", service_name));
|
||||
ar(cereal::make_nvp("Service ID", service_id));
|
||||
ar(cereal::make_nvp("Service port", service_port));
|
||||
ar(cereal::make_nvp("Relevant configs", relevant_configs));
|
||||
}
|
||||
|
||||
ReconfStatus
|
||||
ServiceDetails::sendNewConfigurations(int configuration_id, const string &policy_version)
|
||||
{
|
||||
SendConfigurations new_config(configuration_id, policy_version);
|
||||
|
||||
I_Messaging *messaging = Singleton::Consume<I_Messaging>::by<ServiceController>();
|
||||
Flags<MessageConnConfig> conn_flags;
|
||||
conn_flags.setFlag(MessageConnConfig::ONE_TIME_CONN);
|
||||
bool res = messaging->sendObject(
|
||||
new_config,
|
||||
I_Messaging::Method::POST,
|
||||
"127.0.0.1",
|
||||
service_port,
|
||||
conn_flags,
|
||||
"/set-new-configuration"
|
||||
);
|
||||
if (!res) {
|
||||
if(!isServiceActive()) {
|
||||
dbgDebug(D_ORCHESTRATOR) << "Service " << service_name << " is inactive";
|
||||
return ReconfStatus::INACTIVE;
|
||||
}
|
||||
dbgDebug(D_ORCHESTRATOR) << "Service " << service_name << " didn't respond to new configuration request";
|
||||
return ReconfStatus::FAILED;
|
||||
}
|
||||
auto service_details = Singleton::Consume<I_ServiceController>::by<ServiceDetails>();
|
||||
|
||||
if (new_config.finished.get()) {
|
||||
if (!new_config.error.get()) {
|
||||
service_details->startReconfStatus(new_config.id.get(), ReconfStatus::SUCCEEDED, service_name, service_id);
|
||||
dbgDebug(D_ORCHESTRATOR) << "Loading service configuration succeeded for service " << service_name;
|
||||
return ReconfStatus::SUCCEEDED;
|
||||
} else {
|
||||
string log_name = "Agent could not update policy to version " +
|
||||
service_details->getUpdatePolicyVersion() +
|
||||
". " +
|
||||
(new_config.error_message.isActive() ? "Additional details: " + new_config.error_message.get() : "");
|
||||
LogGen(
|
||||
log_name,
|
||||
Audience::SECURITY,
|
||||
Severity::CRITICAL,
|
||||
Priority::HIGH,
|
||||
Tags::ORCHESTRATOR
|
||||
)
|
||||
<< LogField("ServiceName", service_name)
|
||||
<< LogField("policyVersion", service_details->getPolicyVersion());
|
||||
|
||||
service_details->startReconfStatus(new_config.id.get(), ReconfStatus::FAILED, service_name, service_id);
|
||||
dbgDebug(D_ORCHESTRATOR)
|
||||
<< "Loading service configuration failed for service "
|
||||
<< service_name
|
||||
<< " with error: "
|
||||
<< (new_config.error_message.isActive() ? new_config.error_message.get() : "");
|
||||
return ReconfStatus::FAILED;
|
||||
}
|
||||
}
|
||||
dbgDebug(D_ORCHESTRATOR) << "Loading service configuration is in progress for service: " << service_name;
|
||||
service_details->startReconfStatus(new_config.id.get(), ReconfStatus::IN_PROGRESS, service_name, service_id);
|
||||
return ReconfStatus::IN_PROGRESS;
|
||||
}
|
||||
|
||||
void
|
||||
SetNanoServiceConfig::doCall()
|
||||
{
|
||||
dbgFlow(D_ORCHESTRATOR)
|
||||
<< "Received registration request from service. Service name: "
|
||||
<< service_name.get()
|
||||
<< ", service listening port: "
|
||||
<< service_listening_port.get();
|
||||
|
||||
I_ServiceController *i_service_controller = Singleton::Consume<I_ServiceController>::from<ServiceController>();
|
||||
i_service_controller->registerServiceConfig(
|
||||
service_name,
|
||||
service_listening_port,
|
||||
expected_configurations,
|
||||
service_id.isActive() ? service_id.get() : service_name.get()
|
||||
);
|
||||
|
||||
status = true;
|
||||
}
|
||||
|
||||
class ServiceController::Impl
|
||||
:
|
||||
Singleton::Provide<I_ServiceController>::From<ServiceController>,
|
||||
Singleton::Consume<I_OrchestrationTools>
|
||||
{
|
||||
public:
|
||||
void init();
|
||||
|
||||
bool
|
||||
updateServiceConfiguration(
|
||||
const string &new_policy_path,
|
||||
const string &new_settings_path,
|
||||
const vector<string> &new_data_files,
|
||||
const string &tenant_id
|
||||
) override;
|
||||
|
||||
bool isServiceInstalled(const string &service_name) override;
|
||||
|
||||
void registerServiceConfig(
|
||||
const string &service_name,
|
||||
PortNumber listening_port,
|
||||
const vector<string> &relevant_configurations,
|
||||
const string &service_id
|
||||
) override;
|
||||
|
||||
const string & getPolicyVersion() const override;
|
||||
const string & getUpdatePolicyVersion() const override;
|
||||
void updateReconfStatus(int id, ReconfStatus status) override;
|
||||
void startReconfStatus(
|
||||
int id,
|
||||
ReconfStatus status,
|
||||
const string &service_name,
|
||||
const string &service_id
|
||||
) override;
|
||||
|
||||
private:
|
||||
void cleanUpVirtualFiles();
|
||||
void refreshPendingServices();
|
||||
|
||||
bool sendSignalForServices(const set<string> &nano_services_to_update, const string &policy_version);
|
||||
|
||||
bool updateServiceConfigurationFile(
|
||||
const string &configuration_name,
|
||||
const string &configuration_file_path,
|
||||
const string &new_configuration_path);
|
||||
|
||||
ReconfStatus getUpdatedReconfStatus();
|
||||
Maybe<ServiceDetails> getServiceDetails(const string &service_name);
|
||||
map<string, PortNumber> getServiceToPortMap();
|
||||
|
||||
template<class Archive>
|
||||
void serializeRegisterServices(Archive &ar) { ar(pending_services); }
|
||||
|
||||
void loadRegisteredServicesFromFile();
|
||||
void writeRegisteredServicesToFile();
|
||||
|
||||
int configuration_id = 0;
|
||||
map<string, ServiceDetails> registered_services;
|
||||
map<string, ServiceDetails> pending_services;
|
||||
string policy_version;
|
||||
string update_policy_version;
|
||||
string settings_path;
|
||||
map<int, ReconfStatus> services_reconf_status;
|
||||
map<int, string> services_reconf_names;
|
||||
map<int, string> services_reconf_ids;
|
||||
string filesystem_prefix;
|
||||
};
|
||||
|
||||
class GetServicesPorts : public ServerRest
|
||||
{
|
||||
public:
|
||||
void
|
||||
doCall()
|
||||
{
|
||||
stringstream output;
|
||||
auto ports_map = Singleton::Consume<I_ServiceController>::from<ServiceController>()->getServiceToPortMap();
|
||||
for (auto const& entry: ports_map) {
|
||||
string service = entry.first;
|
||||
replace(service.begin(), service.end(), ' ', '-');
|
||||
output << service << ":";
|
||||
output << entry.second << ",";
|
||||
}
|
||||
ports_list = output.str();
|
||||
}
|
||||
|
||||
S2C_PARAM(string, ports_list);
|
||||
};
|
||||
|
||||
Maybe<ServiceDetails>
|
||||
ServiceController::Impl::getServiceDetails(const string &service_id)
|
||||
{
|
||||
auto iter = registered_services.find(service_id);
|
||||
if (iter != registered_services.end()) return iter->second;
|
||||
|
||||
return genError("did not find service details for the provided service name. service id: " + service_id);
|
||||
}
|
||||
|
||||
ReconfStatus
|
||||
ServiceController::Impl::getUpdatedReconfStatus()
|
||||
{
|
||||
ReconfStatus res = ReconfStatus::SUCCEEDED;
|
||||
|
||||
for(auto &service_and_reconf_status : services_reconf_status) {
|
||||
string service_id = services_reconf_ids[service_and_reconf_status.first];
|
||||
auto maybe_service = getServiceDetails(service_id);
|
||||
|
||||
if (!maybe_service.ok()) {
|
||||
dbgWarning(D_ORCHESTRATOR) << "Unable to get service details. Error: " << maybe_service.getErr();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!maybe_service.unpack().isServiceActive()) {
|
||||
dbgInfo(D_ORCHESTRATOR)
|
||||
<< "Service is not active, removing from registered services list. Service: "
|
||||
<< services_reconf_names[service_and_reconf_status.first]
|
||||
<< "ID: "
|
||||
<< service_id;
|
||||
registered_services.erase(service_id);
|
||||
service_and_reconf_status.second = ReconfStatus::INACTIVE;
|
||||
writeRegisteredServicesToFile();
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (res < service_and_reconf_status.second) res = service_and_reconf_status.second;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void
|
||||
ServiceController::Impl::init()
|
||||
{
|
||||
auto rest = Singleton::Consume<I_RestApi>::by<ServiceController>();
|
||||
rest->addRestCall<SetNanoServiceConfig>(RestAction::SET, "nano-service-config");
|
||||
rest->addRestCall<GetServicesPorts>(RestAction::SHOW, "all-service-ports");
|
||||
rest->addRestCall<ServiceReconfStatusMonitor>(RestAction::SET, "reconf-status");
|
||||
|
||||
Singleton::Consume<I_MainLoop>::by<ServiceController>()->addRecurringRoutine(
|
||||
I_MainLoop::RoutineType::System,
|
||||
chrono::seconds(
|
||||
getConfigurationWithDefault<int>(
|
||||
86400,
|
||||
"orchestration",
|
||||
"Cleanup virtual tenant seconds interval"
|
||||
)
|
||||
),
|
||||
[this] () { cleanUpVirtualFiles(); },
|
||||
"Cleanup virtual tenants"
|
||||
);
|
||||
|
||||
filesystem_prefix = getFilesystemPathConfig();
|
||||
|
||||
loadRegisteredServicesFromFile();
|
||||
}
|
||||
|
||||
void
|
||||
ServiceController::Impl::loadRegisteredServicesFromFile()
|
||||
{
|
||||
auto registered_services_file = getConfigurationWithDefault<string>(
|
||||
filesystem_prefix + "/conf/orchestrations_registered_services.json",
|
||||
"orchestration",
|
||||
"Orchestration registered services"
|
||||
);
|
||||
auto maybe_registered_services_str = Singleton::Consume<I_OrchestrationTools>::by<ServiceController::Impl>()->
|
||||
readFile(registered_services_file);
|
||||
if (!maybe_registered_services_str.ok()) {
|
||||
dbgTrace(D_ORCHESTRATOR)
|
||||
<< "could not read file. File: "
|
||||
<< registered_services_file
|
||||
<< " Error: " << maybe_registered_services_str.getErr();
|
||||
return;
|
||||
}
|
||||
|
||||
stringstream ss(maybe_registered_services_str.unpack());
|
||||
cereal::JSONInputArchive ar(ss);
|
||||
ar(cereal::make_nvp("Registered Services", pending_services));
|
||||
|
||||
dbgInfo(D_ORCHESTRATOR)
|
||||
<< "Orchestration pending services loaded from file."
|
||||
<< " File: "
|
||||
<< registered_services_file
|
||||
<< ". Registered Services:";
|
||||
|
||||
for (const auto &id_service_pair : pending_services) {
|
||||
const auto &service = id_service_pair.second;
|
||||
dbgInfo(D_ORCHESTRATOR)
|
||||
<< "Service name: "
|
||||
<< service.getServiceName()
|
||||
<< ", Service ID: "
|
||||
<< service.getServiceID()
|
||||
<< ", Service port: "
|
||||
<< service.getPort();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ServiceController::Impl::writeRegisteredServicesToFile()
|
||||
{
|
||||
dbgFlow(D_ORCHESTRATOR);
|
||||
auto registered_services_file = getConfigurationWithDefault<string>(
|
||||
filesystem_prefix + "/conf/orchestrations_registered_services.json",
|
||||
"orchestration",
|
||||
"Orchestration registered services"
|
||||
);
|
||||
|
||||
ofstream ss(registered_services_file);
|
||||
cereal::JSONOutputArchive ar(ss);
|
||||
ar(cereal::make_nvp("Registered Services", registered_services));
|
||||
|
||||
dbgInfo(D_ORCHESTRATOR)
|
||||
<< "Orchestration registered services file has been updated. File: "
|
||||
<< registered_services_file
|
||||
<< ". Registered Services:";
|
||||
|
||||
for (const auto &id_service_pair : registered_services) {
|
||||
const auto &service = id_service_pair.second;
|
||||
dbgInfo(D_ORCHESTRATOR)
|
||||
<< "Service name: "
|
||||
<< service.getServiceName()
|
||||
<< ", Service ID: "
|
||||
<< service.getServiceID()
|
||||
<< ", Service port: "
|
||||
<< service.getPort();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ServiceController::Impl::cleanUpVirtualFiles()
|
||||
{
|
||||
const string file_list_cmd =
|
||||
"ls " +
|
||||
getConfigurationWithDefault<string>(
|
||||
filesystem_prefix + "/conf",
|
||||
"orchestration",
|
||||
"Configuration directory"
|
||||
) +
|
||||
" | grep 'tenant_*' | cut -d '_' -f 2";
|
||||
|
||||
auto shell_cmd = Singleton:: Consume<I_ShellCmd>::by<ServiceController>();
|
||||
auto tenant_manager = Singleton::Consume<I_TenantManager>::by<ServiceController>();
|
||||
|
||||
auto result = shell_cmd->getExecOutput(file_list_cmd);
|
||||
if (!result.ok()) return;
|
||||
|
||||
set<string> tenants_on_agent;
|
||||
|
||||
istringstream parsig(*result);
|
||||
while (!parsig.eof()) {
|
||||
string tenant_id;
|
||||
getline(parsig, tenant_id);
|
||||
if (!tenant_id.empty()) tenants_on_agent.insert(tenant_id);
|
||||
}
|
||||
|
||||
for (const auto &active_tenant: tenant_manager->fetchActiveTenants()) {
|
||||
tenants_on_agent.erase(active_tenant);
|
||||
}
|
||||
|
||||
for (const auto &none_active_tenant: tenants_on_agent) {
|
||||
// remove files;
|
||||
string settings_file = filesystem_prefix + "/conf/"+ none_active_tenant + "_settings.json";
|
||||
string tenant_dir = filesystem_prefix + "/conf/tenant_"+ none_active_tenant;
|
||||
|
||||
Singleton::Consume<I_OrchestrationTools>::by<ServiceController>()->removeFile(settings_file);
|
||||
rmdir(tenant_dir.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
map<string, PortNumber>
|
||||
ServiceController::Impl::getServiceToPortMap()
|
||||
{
|
||||
map<string, PortNumber> ports_map;
|
||||
for (auto const& entry: registered_services) {
|
||||
const string &service = entry.first;
|
||||
PortNumber port = entry.second.getPort();
|
||||
ports_map[service] = port;
|
||||
}
|
||||
|
||||
for (auto const& entry: pending_services) {
|
||||
const string &service = entry.first;
|
||||
PortNumber port = entry.second.getPort();
|
||||
ports_map[service] = port;
|
||||
}
|
||||
|
||||
return ports_map;
|
||||
}
|
||||
|
||||
void
|
||||
ServiceController::Impl::registerServiceConfig(
|
||||
const string &service_name,
|
||||
PortNumber listening_port,
|
||||
const vector<string> &relevant_configurations,
|
||||
const string &service_id)
|
||||
{
|
||||
ServiceDetails service_config(
|
||||
service_name,
|
||||
listening_port,
|
||||
relevant_configurations,
|
||||
service_id
|
||||
);
|
||||
|
||||
pending_services.erase(service_config.getServiceID());
|
||||
pending_services.insert({service_config.getServiceID(), service_config});
|
||||
}
|
||||
|
||||
bool
|
||||
ServiceController::Impl::isServiceInstalled(const string &service_name)
|
||||
{
|
||||
return
|
||||
registered_services.find(service_name) != registered_services.end() ||
|
||||
pending_services.find(service_name) != pending_services.end();
|
||||
}
|
||||
|
||||
void
|
||||
ServiceController::Impl::refreshPendingServices()
|
||||
{
|
||||
dbgFlow(D_ORCHESTRATOR);
|
||||
if (pending_services.empty()) return;
|
||||
for (const auto &service : pending_services) {
|
||||
registered_services.erase(service.first);
|
||||
registered_services.insert({service.first, service.second});
|
||||
dbgDebug(D_ORCHESTRATOR) << "Successfully registered service. Name: " << service.first;
|
||||
}
|
||||
pending_services.clear();
|
||||
|
||||
writeRegisteredServicesToFile();
|
||||
}
|
||||
|
||||
bool
|
||||
ServiceController::Impl::updateServiceConfiguration(
|
||||
const string &new_policy_path,
|
||||
const string &new_settings_path,
|
||||
const vector<string> &new_data_files,
|
||||
const string &tenant_id)
|
||||
{
|
||||
dbgFlow(D_ORCHESTRATOR)
|
||||
<< "new_policy_path: "
|
||||
<< new_policy_path
|
||||
<< ", new_settings_path: "
|
||||
<< new_settings_path
|
||||
<< ", new_data_files: "
|
||||
<< makeSeparatedStr(new_data_files, ",")
|
||||
<< ". tenant_id: "
|
||||
<< tenant_id;
|
||||
|
||||
if (!new_settings_path.empty()) {
|
||||
settings_path = new_settings_path;
|
||||
}
|
||||
|
||||
refreshPendingServices();
|
||||
|
||||
set<string> nano_services_to_update;
|
||||
for (const auto &service : registered_services) {
|
||||
if (new_settings_path != "") {
|
||||
nano_services_to_update.insert(service.first);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const string &data : new_data_files) {
|
||||
dbgTrace(D_ORCHESTRATOR) << "data: " << data;
|
||||
if (service.second.isConfigurationRelevant(data)) {
|
||||
dbgTrace(D_ORCHESTRATOR)
|
||||
<< "data has relevant configuration, will update the service: "
|
||||
<< service.first;
|
||||
nano_services_to_update.insert(service.first);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (new_policy_path == "") {
|
||||
dbgDebug(D_ORCHESTRATOR) << "Policy file was not updated. Sending reload command regarding settings and data";
|
||||
|
||||
return sendSignalForServices(nano_services_to_update, "");
|
||||
}
|
||||
|
||||
I_OrchestrationTools *orchestration_tools = Singleton::Consume<I_OrchestrationTools>::by<ServiceController>();
|
||||
Maybe<string> loaded_json = orchestration_tools->readFile(new_policy_path);
|
||||
if (!loaded_json.ok()) {
|
||||
dbgWarning(D_ORCHESTRATOR)
|
||||
<< "Failed to load new file: "
|
||||
<< new_policy_path
|
||||
<< ". Error: "
|
||||
<< loaded_json.getErr();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto all_security_policies = orchestration_tools->jsonObjectSplitter(loaded_json.unpack(), tenant_id);
|
||||
|
||||
if (!all_security_policies.ok()) {
|
||||
dbgWarning(D_ORCHESTRATOR)
|
||||
<< "Failed to parse json file: "
|
||||
<< new_policy_path
|
||||
<< ". Error: "
|
||||
<< all_security_policies.getErr();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool was_policy_updated = true;
|
||||
const string version_param = "version";
|
||||
string version_value;
|
||||
|
||||
for (auto &single_policy : all_security_policies.unpack()) {
|
||||
if (single_policy.first == version_param) {
|
||||
version_value = single_policy.second;
|
||||
version_value.erase(remove(version_value.begin(), version_value.end(), '\"'), version_value.end());
|
||||
update_policy_version = version_value;
|
||||
continue;
|
||||
}
|
||||
|
||||
dbgDebug(D_ORCHESTRATOR) << "Starting to update policy file. Policy type: " << single_policy.first;
|
||||
|
||||
string dir = getConfigurationWithDefault<string>(
|
||||
filesystem_prefix + "/conf",
|
||||
"orchestration",
|
||||
"Configuration directory"
|
||||
);
|
||||
|
||||
if (tenant_id != "") {
|
||||
dir = dir + "/tenant_" + tenant_id;
|
||||
if (!orchestration_tools->doesDirectoryExist(dir)) {
|
||||
if (orchestration_tools->createDirectory(dir)) {
|
||||
dbgTrace(D_ORCHESTRATOR) << "Created new configuration directory for tenant " << tenant_id;
|
||||
} else {
|
||||
dbgError(D_ORCHESTRATOR) << "Failed to create configuration directory for tenant "<< tenant_id;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string policy_file_path = getPolicyConfigPath(single_policy.first, Config::ConfigFileType::Policy, tenant_id);
|
||||
|
||||
auto update_config_result = updateServiceConfigurationFile(
|
||||
single_policy.first,
|
||||
policy_file_path,
|
||||
single_policy.second
|
||||
);
|
||||
|
||||
if (!update_config_result) {
|
||||
dbgWarning(D_ORCHESTRATOR) << "Failed to update policy file. Policy name: " << single_policy.first;
|
||||
was_policy_updated = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
dbgInfo(D_ORCHESTRATOR) << "Successfully updated policy file. Policy name: " << single_policy.first;
|
||||
|
||||
auto orc_status = Singleton::Consume<I_OrchestrationStatus>::by<ServiceController>();
|
||||
orc_status->setServiceConfiguration(
|
||||
single_policy.first,
|
||||
policy_file_path,
|
||||
OrchestrationStatusConfigType::POLICY
|
||||
);
|
||||
|
||||
if (tenant_id != "") {
|
||||
auto instances = Singleton::Consume<I_TenantManager>::by<ServiceController>()->getInstances(tenant_id);
|
||||
for (const auto &instance_id: instances) {
|
||||
auto relevant_service = registered_services.find(instance_id);
|
||||
if (relevant_service == registered_services.end()) {
|
||||
dbgWarning(D_ORCHESTRATOR) << "Could not find registered service. Service Id: " << instance_id;
|
||||
continue;
|
||||
}
|
||||
if (relevant_service->second.isConfigurationRelevant(single_policy.first)) {
|
||||
nano_services_to_update.insert(instance_id);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (const auto &service : registered_services) {
|
||||
if (service.second.isConfigurationRelevant(single_policy.first)) {
|
||||
nano_services_to_update.insert(service.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
was_policy_updated &= sendSignalForServices(nano_services_to_update, version_value);
|
||||
|
||||
dbgTrace(D_ORCHESTRATOR) << "was_policy_updated: " << (was_policy_updated ? "true" : "false");
|
||||
|
||||
if (was_policy_updated) {
|
||||
string config_file_path;
|
||||
string base_path = filesystem_prefix + "/conf/" + (tenant_id != "" ? "tenant_" + tenant_id + "/" : "");
|
||||
config_file_path = getConfigurationWithDefault<string>(
|
||||
base_path + "policy.json",
|
||||
"orchestration",
|
||||
"Policy file path"
|
||||
);
|
||||
|
||||
if (new_policy_path.compare(config_file_path) == 0) {
|
||||
dbgDebug(D_ORCHESTRATOR) << "Enforcing the default policy file";
|
||||
policy_version = version_value;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
string backup_ext = getConfigurationWithDefault<string>(".bk", "orchestration", "Backup file extension");
|
||||
|
||||
// Save the new configuration file.
|
||||
if (!orchestration_tools->copyFile(new_policy_path, config_file_path)) {
|
||||
dbgWarning(D_ORCHESTRATOR) << "Failed to save the policy file.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Backup the current configuration file.
|
||||
uint max_backup_attempts = 3;
|
||||
bool is_backup_succeed = false;
|
||||
string backup_file = config_file_path + backup_ext;
|
||||
I_MainLoop *mainloop = Singleton::Consume<I_MainLoop>::by<ServiceController>();
|
||||
|
||||
for (size_t i = 0; i < max_backup_attempts; i++) {
|
||||
if (orchestration_tools->copyFile(new_policy_path, backup_file)) {
|
||||
is_backup_succeed = true;
|
||||
break;
|
||||
}
|
||||
mainloop->yield(false);
|
||||
}
|
||||
|
||||
if (!is_backup_succeed) {
|
||||
dbgWarning(D_ORCHESTRATOR) << "Failed to back up the policy file.";
|
||||
return false;
|
||||
}
|
||||
|
||||
policy_version = version_value;
|
||||
}
|
||||
|
||||
return was_policy_updated;
|
||||
}
|
||||
|
||||
bool
|
||||
ServiceController::Impl::sendSignalForServices(
|
||||
const set<string> &nano_services_to_update,
|
||||
const string &policy_version)
|
||||
{
|
||||
dbgFlow(D_ORCHESTRATOR);
|
||||
for (auto &service_id : nano_services_to_update) {
|
||||
auto nano_service = registered_services.find(service_id);
|
||||
if (nano_service == registered_services.end()) {
|
||||
dbgWarning(D_ORCHESTRATOR) << "Could not find registered service. Service Id: " << service_id;
|
||||
continue;
|
||||
}
|
||||
|
||||
++configuration_id;
|
||||
auto reconf_status = nano_service->second.sendNewConfigurations(configuration_id, policy_version);
|
||||
|
||||
if (reconf_status == ReconfStatus::INACTIVE) {
|
||||
dbgWarning(D_ORCHESTRATOR) << "Erasing details regarding inactive service " << service_id;
|
||||
registered_services.erase(service_id);
|
||||
writeRegisteredServicesToFile();
|
||||
}
|
||||
|
||||
if (reconf_status == ReconfStatus::FAILED) {
|
||||
dbgDebug(D_ORCHESTRATOR) << "The reconfiguration failed for serivce " << service_id;
|
||||
services_reconf_status.clear();
|
||||
services_reconf_names.clear();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int reconf_timeout = getConfigurationWithDefault(600, "orchestration", "Reconfiguration timeout seconds");
|
||||
auto timer = Singleton::Consume<I_TimeGet>::by<ServiceController>();
|
||||
auto current_timeout = timer->getMonotonicTime() + chrono::seconds(reconf_timeout);
|
||||
while(timer->getMonotonicTime() < current_timeout) {
|
||||
switch (getUpdatedReconfStatus()) {
|
||||
case ReconfStatus::SUCCEEDED: {
|
||||
dbgDebug(D_ORCHESTRATOR) << "The reconfiguration was successfully completed for all the services";
|
||||
services_reconf_status.clear();
|
||||
services_reconf_names.clear();
|
||||
return true;
|
||||
}
|
||||
case ReconfStatus::IN_PROGRESS: {
|
||||
dbgTrace(D_ORCHESTRATOR) << "Reconfiguration in progress...";
|
||||
Singleton::Consume<I_MainLoop>::by<ServiceController>()->yield(chrono::seconds(2));
|
||||
break;
|
||||
}
|
||||
case ReconfStatus::FAILED: {
|
||||
for(auto &status : services_reconf_status) {
|
||||
if (status.second == ReconfStatus::FAILED) {
|
||||
dbgDebug(D_ORCHESTRATOR)
|
||||
<< "The reconfiguration failed for serivce "
|
||||
<< services_reconf_names[status.first];
|
||||
}
|
||||
}
|
||||
services_reconf_status.clear();
|
||||
services_reconf_names.clear();
|
||||
return false;
|
||||
}
|
||||
case ReconfStatus::INACTIVE: {
|
||||
dbgError(D_ORCHESTRATOR) << "Reached inactive state in the middle of reconfiguration!";
|
||||
services_reconf_status.clear();
|
||||
services_reconf_names.clear();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dbgDebug(D_ORCHESTRATOR) << "The reconfiguration has reached a timeout";
|
||||
services_reconf_status.clear();
|
||||
services_reconf_names.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
ServiceController::Impl::updateServiceConfigurationFile(
|
||||
const string &configuration_name,
|
||||
const string &configuration_file_path,
|
||||
const string &new_configuration_path)
|
||||
{
|
||||
|
||||
dbgFlow(D_ORCHESTRATOR) << "Updating configuration. Config Name: " << configuration_name;
|
||||
|
||||
I_OrchestrationTools *orchestration_tools = Singleton::Consume<I_OrchestrationTools>::by<ServiceController>();
|
||||
if (orchestration_tools->doesFileExist(configuration_file_path)) {
|
||||
Maybe<string> old_configuration = orchestration_tools->readFile(configuration_file_path);
|
||||
if (old_configuration.ok()) {
|
||||
bool service_changed = old_configuration.unpack().compare(new_configuration_path) != 0;
|
||||
if (service_changed == false) {
|
||||
dbgDebug(D_ORCHESTRATOR) << "There is no update for policy file: " << configuration_file_path;
|
||||
return true;
|
||||
}
|
||||
dbgDebug(D_ORCHESTRATOR)
|
||||
<< "Starting to update " << configuration_file_path << " to " << new_configuration_path;
|
||||
string old_configuration_backup_path = configuration_file_path + getConfigurationWithDefault<string>(
|
||||
".bk",
|
||||
"orchestration",
|
||||
"Backup file extension"
|
||||
);
|
||||
if (orchestration_tools->copyFile(configuration_file_path, old_configuration_backup_path)) {
|
||||
dbgDebug(D_ORCHESTRATOR) << "Backup of policy file has been created in: " << configuration_file_path;
|
||||
} else {
|
||||
dbgWarning(D_ORCHESTRATOR) << "Failed to backup policy file";
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
dbgWarning(D_ORCHESTRATOR)
|
||||
<< "Failed to read current policy file "
|
||||
<< configuration_file_path
|
||||
<< ". Error: "
|
||||
<< old_configuration.getErr();
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (orchestration_tools->writeFile(new_configuration_path, configuration_file_path)) {
|
||||
dbgDebug(D_ORCHESTRATOR) << "New policy file has been saved in: " << configuration_file_path;
|
||||
} else {
|
||||
dbgWarning(D_ORCHESTRATOR) << "Failed to save new policy file";
|
||||
return false;
|
||||
}
|
||||
|
||||
dbgInfo(D_ORCHESTRATOR) << "Successfully updated policy file: " << configuration_file_path;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ServiceController::ServiceController() : Component("ServiceController"), pimpl(make_unique<Impl>()) {}
|
||||
|
||||
ServiceController::~ServiceController() {}
|
||||
|
||||
void
|
||||
ServiceController::init()
|
||||
{
|
||||
pimpl->init();
|
||||
}
|
||||
|
||||
const string &
|
||||
ServiceController::Impl::getPolicyVersion() const
|
||||
{
|
||||
return policy_version;
|
||||
}
|
||||
|
||||
const string &
|
||||
ServiceController::Impl::getUpdatePolicyVersion() const
|
||||
{
|
||||
return update_policy_version;
|
||||
}
|
||||
|
||||
void
|
||||
ServiceController::Impl::updateReconfStatus(int id, ReconfStatus status)
|
||||
{
|
||||
if (services_reconf_status.find(id) == services_reconf_status.end()) {
|
||||
dbgError(D_ORCHESTRATOR) << "Service reconfiguration monitor received illegal id :" << id;
|
||||
return;
|
||||
}
|
||||
services_reconf_status[id] = status;
|
||||
}
|
||||
|
||||
void
|
||||
ServiceController::Impl::startReconfStatus(
|
||||
int id,
|
||||
ReconfStatus status,
|
||||
const string &service_name,
|
||||
const string &service_id)
|
||||
{
|
||||
services_reconf_status.emplace(id, status);
|
||||
services_reconf_names.emplace(id, service_name);
|
||||
services_reconf_ids.emplace(id, service_id);
|
||||
}
|
||||
|
||||
SASAL_END
|
||||
@@ -0,0 +1,7 @@
|
||||
link_directories(${BOOST_ROOT}/lib)
|
||||
|
||||
add_unit_test(
|
||||
service_controller_ut
|
||||
"service_controller_ut.cc"
|
||||
"service_controller;rest;config;environment;metric;event_is;shell_cmd;orchestration_modules;logging;agent_details;-lboost_regex"
|
||||
)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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)
|
||||
572
components/security_apps/orchestration/update_communication/fog_authenticator.cc
Executable file
572
components/security_apps/orchestration/update_communication/fog_authenticator.cc
Executable 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 ®_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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;"
|
||||
)
|
||||
@@ -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());
|
||||
}
|
||||
13
components/security_apps/waap/CMakeLists.txt
Executable file
13
components/security_apps/waap/CMakeLists.txt
Executable file
@@ -0,0 +1,13 @@
|
||||
add_library(waap
|
||||
waap_component.cc
|
||||
waap_component_impl.cc
|
||||
first_request_object.cc
|
||||
)
|
||||
|
||||
add_subdirectory(waap_clib)
|
||||
add_subdirectory(reputation)
|
||||
|
||||
include_directories(include)
|
||||
include_directories(reputation)
|
||||
|
||||
install(DIRECTORY resources DESTINATION http_transaction_handler_service USE_SOURCE_PERMISSIONS)
|
||||
57
components/security_apps/waap/first_request_object.cc
Executable file
57
components/security_apps/waap/first_request_object.cc
Executable file
@@ -0,0 +1,57 @@
|
||||
// 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 "first_request_object.h"
|
||||
#include "tag_and_enum_management.h"
|
||||
|
||||
FirstRequestNotificationObject::FirstRequestNotificationObject(
|
||||
std::string asset_id,
|
||||
std::string asset_name,
|
||||
ReportIS::Severity severity
|
||||
):
|
||||
m_asset_id(asset_id),
|
||||
m_asset_name(asset_name),
|
||||
m_severity(severity)
|
||||
{}
|
||||
|
||||
FirstRequestNotificationObject::~FirstRequestNotificationObject()
|
||||
{}
|
||||
|
||||
void FirstRequestNotificationObject::serialize(cereal::JSONOutputArchive& ar) const
|
||||
{
|
||||
ar.setNextName("notificationConsumerData");
|
||||
ar.startNode();
|
||||
ar.setNextName("firstRequestNotificationConsumers");
|
||||
ar.startNode();
|
||||
ar(cereal::make_nvp("assetId", m_asset_id));
|
||||
ar(cereal::make_nvp("assetName", m_asset_name));
|
||||
ar(cereal::make_nvp("originalEventSeverity", TagAndEnumManagement::convertToString(m_severity)));
|
||||
ar.finishNode();
|
||||
ar.finishNode();
|
||||
}
|
||||
|
||||
std::string FirstRequestNotificationObject::toString() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
{
|
||||
cereal::JSONOutputArchive ar(ss);
|
||||
serialize(ar);
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const FirstRequestNotificationObject& obj)
|
||||
{
|
||||
return os << obj.toString();
|
||||
}
|
||||
42
components/security_apps/waap/first_request_object.h
Executable file
42
components/security_apps/waap/first_request_object.h
Executable file
@@ -0,0 +1,42 @@
|
||||
// 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 __FIRST_REQUEST_NOTIFICATION_OBJECT_H__
|
||||
#define __FIRST_REQUEST_NOTIFICATION_OBJECT_H__
|
||||
|
||||
#include <string>
|
||||
#include <ostream>
|
||||
#include "cereal/archives/json.hpp"
|
||||
#include "report/report.h"
|
||||
|
||||
class FirstRequestNotificationObject
|
||||
{
|
||||
public:
|
||||
explicit FirstRequestNotificationObject(
|
||||
std::string asset_id,
|
||||
std::string asset_name,
|
||||
ReportIS::Severity severity
|
||||
);
|
||||
virtual ~FirstRequestNotificationObject();
|
||||
void serialize(cereal::JSONOutputArchive& ar) const;
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const FirstRequestNotificationObject& obj);
|
||||
|
||||
private:
|
||||
std::string toString() const;
|
||||
|
||||
std::string m_asset_id;
|
||||
std::string m_asset_name;
|
||||
ReportIS::Severity m_severity;
|
||||
};
|
||||
#endif
|
||||
30
components/security_apps/waap/include/WaapDefines.h
Executable file
30
components/security_apps/waap/include/WaapDefines.h
Executable file
@@ -0,0 +1,30 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#define BACKUP_DIRECTORY_PATH "/etc/cp/conf/waap/"
|
||||
// reduce from 2048 in order to accomodate in 10K max log size in Kibana
|
||||
#define MAX_LOG_FIELD_SIZE 1536
|
||||
// maximum bytes response body log field size can reduce from request body log
|
||||
#define MIN_RESP_BODY_LOG_FIELD_SIZE (std::size_t{500})
|
||||
// size of clean values LRU cache
|
||||
#define SIGS_APPLY_CLEAN_CACHE_CAPACITY 4096
|
||||
// size of suspicious values LRU cache
|
||||
#define SIGS_APPLY_SUSPICIOUS_CACHE_CAPACITY 4096
|
||||
// size of SampleType cache capacity
|
||||
#define SIGS_SAMPLE_TYPE_CACHE_CAPACITY 4096
|
||||
|
||||
// ScoreBuilder pool names
|
||||
#define KEYWORDS_SCORE_POOL_BASE "base_scores"
|
||||
#define KEYWORDS_SCORE_POOL_HEADERS "headers_scores"
|
||||
24
components/security_apps/waap/include/i_deepAnalyzer.h
Executable file
24
components/security_apps/waap/include/i_deepAnalyzer.h
Executable file
@@ -0,0 +1,24 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
class IWaf2Transaction;
|
||||
class IWaapConfig;
|
||||
struct AnalysisResult;
|
||||
|
||||
class I_DeepAnalyzer {
|
||||
public:
|
||||
virtual AnalysisResult analyzeData(IWaf2Transaction* waf2Trans, const IWaapConfig* pSitePolicy) = 0;
|
||||
virtual ~I_DeepAnalyzer() {};
|
||||
};
|
||||
26
components/security_apps/waap/include/i_ignoreSources.h
Executable file
26
components/security_apps/waap/include/i_ignoreSources.h
Executable file
@@ -0,0 +1,26 @@
|
||||
// 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 __I_IGNORE_SOURCES_H__
|
||||
#define __I_IGNORE_SOURCES_H__
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
class I_IgnoreSources
|
||||
{
|
||||
public:
|
||||
virtual std::vector<std::string>* getSourcesToIgnore() = 0;
|
||||
virtual bool ready() = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
39
components/security_apps/waap/include/i_indicatorsFilter.h
Executable file
39
components/security_apps/waap/include/i_indicatorsFilter.h
Executable file
@@ -0,0 +1,39 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include "../waap_clib/WaapKeywords.h"
|
||||
#include "i_serialize.h"
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
class IWaf2Transaction;
|
||||
|
||||
class I_IndicatorsFilter{
|
||||
public:
|
||||
virtual ~I_IndicatorsFilter() { }
|
||||
|
||||
// filters indicators from keywords vector
|
||||
virtual void filterKeywords(
|
||||
const std::string &key,
|
||||
Waap::Keywords::KeywordsSet& keywords,
|
||||
Waap::Keywords::KeywordsVec& filteredKeywords) = 0;
|
||||
|
||||
// register keyword for a specific key
|
||||
virtual void registerKeywords(const std::string& key, Waap::Keywords::KeywordsSet& keyword,
|
||||
IWaf2Transaction* pTransaction) = 0;
|
||||
|
||||
// returns true if the keyword based on the key should be filtered out
|
||||
virtual bool shouldFilterKeyword(const std::string &key, const std::string &keyword) const = 0;
|
||||
};
|
||||
281
components/security_apps/waap/include/i_serialize.h
Executable file
281
components/security_apps/waap/include/i_serialize.h
Executable file
@@ -0,0 +1,281 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
#include <chrono>
|
||||
#include <fstream>
|
||||
#include "i_time_get.h"
|
||||
#include "i_encryptor.h"
|
||||
#include "rest.h"
|
||||
#include "i_messaging.h"
|
||||
#include "i_mainloop.h"
|
||||
#include "i_agent_details.h"
|
||||
|
||||
static const uint max_send_obj_retries = 3;
|
||||
|
||||
USE_DEBUG_FLAG(D_WAAP);
|
||||
|
||||
class RestGetFile : public ClientRest
|
||||
{
|
||||
public:
|
||||
// decrypts and load json
|
||||
bool loadJson(const std::string& json);
|
||||
// gen json and encrypt
|
||||
Maybe<std::string> genJson() const;
|
||||
};
|
||||
|
||||
struct FileMetaData
|
||||
{
|
||||
std::string filename;
|
||||
std::string modified;
|
||||
};
|
||||
|
||||
class RemoteFilesList : public ClientRest
|
||||
{
|
||||
public:
|
||||
RemoteFilesList();
|
||||
|
||||
// parses xml instead of json
|
||||
// extracts a file list in <Contents><Key>
|
||||
bool loadJson(const std::string& xml);
|
||||
|
||||
const std::vector<FileMetaData>& getFilesMetadataList() const;
|
||||
const std::vector<std::string>& getFilesList() const;
|
||||
|
||||
private:
|
||||
RestParam<std::vector<FileMetaData>> files;
|
||||
std::vector<std::string> filesPathsList;
|
||||
};
|
||||
|
||||
|
||||
class I_Serializable {
|
||||
public:
|
||||
virtual void serialize(std::ostream& stream) = 0;
|
||||
virtual void deserialize(std::istream& stream) = 0;
|
||||
};
|
||||
|
||||
class I_RemoteSyncSerialize {
|
||||
public:
|
||||
virtual bool postData() = 0;
|
||||
virtual void pullData(const std::vector<std::string>& files) = 0;
|
||||
virtual void processData() = 0;
|
||||
virtual void postProcessedData() = 0;
|
||||
virtual void pullProcessedData(const std::vector<std::string>& files) = 0;
|
||||
virtual void updateState(const std::vector<std::string>& files) = 0;
|
||||
};
|
||||
|
||||
class I_Backup {
|
||||
public:
|
||||
// open stream and serialize data
|
||||
virtual void saveData() = 0;
|
||||
// open stream and deserialize data
|
||||
virtual void restore() = 0;
|
||||
};
|
||||
|
||||
class SerializeToFileBase :
|
||||
public I_Backup,
|
||||
public I_Serializable
|
||||
{
|
||||
public:
|
||||
SerializeToFileBase(std::string filePath);
|
||||
virtual ~SerializeToFileBase();
|
||||
|
||||
virtual void saveData();
|
||||
virtual void restore();
|
||||
virtual void setFilePath(const std::string &new_file_path);
|
||||
|
||||
protected:
|
||||
// saved file name for testing
|
||||
std::string m_filePath;
|
||||
private:
|
||||
void loadFromFile(std::string filePath);
|
||||
};
|
||||
|
||||
class SerializeToFilePeriodically : public SerializeToFileBase
|
||||
{
|
||||
public:
|
||||
SerializeToFilePeriodically(std::chrono::seconds pollingIntervals, std::string filePath);
|
||||
virtual ~SerializeToFilePeriodically();
|
||||
|
||||
void setInterval(std::chrono::seconds newInterval);
|
||||
|
||||
protected:
|
||||
void backupWorker();
|
||||
|
||||
private:
|
||||
std::chrono::microseconds m_lastSerialization;
|
||||
std::chrono::seconds m_interval;
|
||||
};
|
||||
|
||||
class WaapComponent;
|
||||
|
||||
class SerializeToLocalAndRemoteSyncBase : public I_RemoteSyncSerialize, public SerializeToFileBase
|
||||
{
|
||||
public:
|
||||
SerializeToLocalAndRemoteSyncBase(std::chrono::minutes interval,
|
||||
std::chrono::seconds waitForSync,
|
||||
const std::string& filePath,
|
||||
const std::string& remotePath,
|
||||
const std::string& assetId,
|
||||
const std::string& owner);
|
||||
virtual ~SerializeToLocalAndRemoteSyncBase();
|
||||
|
||||
virtual void restore();
|
||||
|
||||
virtual void syncWorker();
|
||||
|
||||
void setInterval(std::chrono::seconds newInterval);
|
||||
std::chrono::seconds getIntervalDuration() const;
|
||||
void setRemoteSyncEnabled(bool enabled);
|
||||
protected:
|
||||
void mergeProcessedFromRemote();
|
||||
std::string getPostDataUrl();
|
||||
std::string getUri();
|
||||
size_t getIntervalsCount();
|
||||
|
||||
template<typename T>
|
||||
bool sendObject(T &obj, I_Messaging::Method method, std::string uri)
|
||||
{
|
||||
I_Messaging *messaging = Singleton::Consume<I_Messaging>::by<WaapComponent>();
|
||||
I_AgentDetails *agentDetails = Singleton::Consume<I_AgentDetails>::by<WaapComponent>();
|
||||
if (agentDetails->getOrchestrationMode() == OrchestrationMode::OFFLINE) {
|
||||
dbgDebug(D_WAAP) << "offline mode not sending object";
|
||||
return false;
|
||||
}
|
||||
if (agentDetails->getOrchestrationMode() == OrchestrationMode::HYBRID) {
|
||||
Flags <MessageConnConfig> conn_flags;
|
||||
conn_flags.setFlag(MessageConnConfig::EXTERNAL);
|
||||
std::string tenant_header = "X-Tenant-Id: " + agentDetails->getTenantId();
|
||||
|
||||
return messaging->sendObject(
|
||||
obj,
|
||||
method,
|
||||
getSharedStorageHost(),
|
||||
80,
|
||||
conn_flags,
|
||||
uri,
|
||||
tenant_header,
|
||||
nullptr,
|
||||
MessageTypeTag::WAAP_LEARNING);
|
||||
}
|
||||
return messaging->sendObject(
|
||||
obj,
|
||||
method,
|
||||
uri,
|
||||
"",
|
||||
nullptr,
|
||||
true,
|
||||
MessageTypeTag::WAAP_LEARNING);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool sendObjectWithRetry(T &obj, I_Messaging::Method method, std::string uri)
|
||||
{
|
||||
I_MainLoop *mainloop = Singleton::Consume<I_MainLoop>::by<WaapComponent>();
|
||||
for (uint i = 0; i < max_send_obj_retries; i++)
|
||||
{
|
||||
if (sendObject(obj, method, uri))
|
||||
{
|
||||
dbgTrace(D_WAAP) <<
|
||||
"object sent successfully after " << i << " retry attempts";
|
||||
return true;
|
||||
}
|
||||
dbgWarning(D_WAAP) << "Failed to send object. Attempt: " << i;
|
||||
mainloop->yield(true);
|
||||
}
|
||||
dbgError(D_WAAP) << "Failed to send object, reached maximum attempts: " <<
|
||||
max_send_obj_retries;
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool sendNoReplyObject(T &obj, I_Messaging::Method method, std::string uri)
|
||||
{
|
||||
I_Messaging *messaging = Singleton::Consume<I_Messaging>::by<WaapComponent>();
|
||||
I_AgentDetails *agentDetails = Singleton::Consume<I_AgentDetails>::by<WaapComponent>();
|
||||
if (agentDetails->getOrchestrationMode() == OrchestrationMode::OFFLINE) {
|
||||
dbgDebug(D_WAAP) << "offline mode not sending object";
|
||||
return false;
|
||||
}
|
||||
if (agentDetails->getOrchestrationMode() == OrchestrationMode::HYBRID) {
|
||||
Flags<MessageConnConfig> conn_flags;
|
||||
conn_flags.setFlag(MessageConnConfig::EXTERNAL);
|
||||
std::string tenant_header = "X-Tenant-Id: " + agentDetails->getTenantId();
|
||||
return messaging->sendNoReplyObject(
|
||||
obj,
|
||||
method,
|
||||
getSharedStorageHost(),
|
||||
80,
|
||||
conn_flags,
|
||||
uri,
|
||||
tenant_header,
|
||||
nullptr,
|
||||
MessageTypeTag::WAAP_LEARNING);
|
||||
}
|
||||
return messaging->sendNoReplyObject(
|
||||
obj,
|
||||
method,
|
||||
uri,
|
||||
"",
|
||||
nullptr,
|
||||
true,
|
||||
MessageTypeTag::WAAP_LEARNING);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool sendNoReplyObjectWithRetry(T &obj, I_Messaging::Method method, std::string uri)
|
||||
{
|
||||
I_MainLoop *mainloop= Singleton::Consume<I_MainLoop>::by<WaapComponent>();
|
||||
for (uint i = 0; i < max_send_obj_retries; i++)
|
||||
{
|
||||
if (sendNoReplyObject(obj, method, uri))
|
||||
{
|
||||
dbgTrace(D_WAAP) <<
|
||||
"object sent successfully after " << i << " retry attempts";
|
||||
return true;
|
||||
}
|
||||
dbgWarning(D_WAAP) << "Failed to send object. Attempt: " << i;
|
||||
mainloop->yield(true);
|
||||
}
|
||||
dbgError(D_WAAP) << "Failed to send object, reached maximum attempts: " <<
|
||||
max_send_obj_retries;
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string m_remotePath; // Created from tenentId + / + assetId + / + class
|
||||
std::chrono::seconds m_interval;
|
||||
std::string m_owner;
|
||||
|
||||
private:
|
||||
bool localSyncAndProcess();
|
||||
void updateStateFromRemoteService();
|
||||
RemoteFilesList getProcessedFilesList();
|
||||
RemoteFilesList getRemoteProcessedFilesList();
|
||||
std::string getWindowId();
|
||||
bool isBase();
|
||||
std::string getLearningHost();
|
||||
std::string getSharedStorageHost();
|
||||
|
||||
I_MainLoop* m_pMainLoop;
|
||||
std::chrono::microseconds m_waitForSync;
|
||||
uint m_workerRoutineId;
|
||||
size_t m_daysCount;
|
||||
size_t m_windowsCount;
|
||||
size_t m_intervalsCounter;
|
||||
bool m_remoteSyncEnabled;
|
||||
const std::string m_assetId;
|
||||
std::string m_type;
|
||||
std::string m_lastProcessedModified;
|
||||
Maybe<std::string> m_shared_storage_host;
|
||||
Maybe<std::string> m_learning_host;
|
||||
};
|
||||
144
components/security_apps/waap/include/i_transaction.h
Executable file
144
components/security_apps/waap/include/i_transaction.h
Executable file
@@ -0,0 +1,144 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../waap_clib/WaapDecision.h"
|
||||
#include "../include/WaapDefines.h"
|
||||
#include "../waap_clib/Csrf.h"
|
||||
#include "../waap_clib/Waf2Util.h"
|
||||
#include "../waap_clib/WaapOpenRedirect.h"
|
||||
#include "../waap_clib/FpMitigation.h"
|
||||
#include "../waap_clib/DeepParser.h"
|
||||
#include "http_inspection_events.h"
|
||||
|
||||
enum HeaderType {
|
||||
UNKNOWN_HEADER,
|
||||
HOST_HEADER,
|
||||
USER_AGENT_HEADER,
|
||||
COOKIE_HEADER,
|
||||
REFERER_HEADER,
|
||||
CONTENT_TYPE_HEADER,
|
||||
CLEAN_HEADER,
|
||||
OTHER_KNOWN_HEADERS
|
||||
};
|
||||
|
||||
struct AnalysisResult;
|
||||
class WaapAssetState;
|
||||
|
||||
struct Waf2TransactionFlags {
|
||||
bool endResponseHeadersCalled;
|
||||
bool requestDataPushStarted;
|
||||
bool responseDataPushStarted;
|
||||
|
||||
Waf2TransactionFlags():
|
||||
endResponseHeadersCalled(false),
|
||||
requestDataPushStarted(false),
|
||||
responseDataPushStarted(false)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class IWaf2Transaction {
|
||||
public:
|
||||
virtual ~IWaf2Transaction() {}
|
||||
virtual uint64_t getIndex() const = 0;
|
||||
virtual void setIndex(uint64_t index) = 0;
|
||||
virtual std::shared_ptr<WaapAssetState> getAssetState() = 0;
|
||||
virtual IWaapConfig* getSiteConfig() = 0;
|
||||
virtual DeepParser& getDeepParser() = 0;
|
||||
virtual bool get_ignoreScore() const = 0;
|
||||
virtual void addNote(const std::string ¬e) = 0;
|
||||
virtual bool shouldIgnoreOverride(const Waf2ScanResult &res) = 0;
|
||||
virtual bool reportScanResult(const Waf2ScanResult &res) = 0;
|
||||
virtual const std::string getHost() const = 0;
|
||||
virtual Waap::OpenRedirect::State &getOpenRedirectState() = 0;
|
||||
virtual const std::string getLocation() const = 0;
|
||||
virtual const std::string getUserAgent() const = 0;
|
||||
virtual const std::string getParam() const = 0;
|
||||
virtual const std::vector<std::string> getKeywordMatches() const = 0;
|
||||
virtual const std::vector<std::string> getKeywordsCombinations() const = 0;
|
||||
virtual const std::string getContentTypeStr() const = 0;
|
||||
virtual Waap::Util::ContentType getContentType() const = 0;
|
||||
virtual const std::string getKeywordMatchesStr() const = 0;
|
||||
virtual const std::string getSample() const = 0;
|
||||
virtual const std::string getLastScanSample() const = 0;
|
||||
virtual const std::string& getLastScanParamName() const = 0;
|
||||
virtual const std::string getMethod() const = 0;
|
||||
virtual const std::string getHdrContent(std::string hdrName) const = 0;
|
||||
virtual const WaapDecision &getWaapDecision() const = 0;
|
||||
virtual const std::string& getRemoteAddr() const = 0;
|
||||
virtual const std::string getUri() const = 0;
|
||||
virtual const std::string getUriStr() const = 0;
|
||||
virtual const std::string& getSourceIdentifier() const = 0;
|
||||
virtual double getScore() const = 0;
|
||||
virtual const std::vector<double> getScoreArray() const = 0;
|
||||
virtual Waap::CSRF::State& getCsrfState() = 0;
|
||||
virtual ngx_http_cp_verdict_e getUserLimitVerdict() = 0;
|
||||
virtual const std::string getUserLimitVerdictStr() const = 0;
|
||||
virtual const std::string getViolatedUserLimitTypeStr() const = 0;
|
||||
virtual void checkShouldInject() = 0;
|
||||
virtual void completeInjectionResponseBody(std::string& strInjection) = 0;
|
||||
virtual void sendLog() = 0;
|
||||
virtual bool decideAfterHeaders() = 0;
|
||||
virtual int decideFinal(
|
||||
int mode,
|
||||
AnalysisResult &transactionResult,
|
||||
const std::string &poolName=KEYWORDS_SCORE_POOL_BASE,
|
||||
PolicyCounterType fpClassification = UNKNOWN_TYPE) = 0;
|
||||
virtual bool decideResponse() = 0;
|
||||
virtual void clearAllInjectionReasons() = 0;
|
||||
virtual bool shouldInspectResponse() = 0;
|
||||
virtual bool shouldInjectResponse() = 0;
|
||||
virtual bool shouldInjectCSRF() = 0;
|
||||
virtual bool shouldInjectSecurityHeaders() = 0;
|
||||
virtual void handleSecurityHeadersInjection(
|
||||
std::vector<std::pair<std::string, std::string>>& injectHeaderStrs) = 0;
|
||||
virtual void disableShouldInjectSecurityHeaders() = 0;
|
||||
virtual void handleCsrfHeaderInjection(std::string& injectStr) = 0;
|
||||
virtual bool findHtmlTagToInject(const char* data, int data_len, int& pos) = 0;
|
||||
virtual bool isHtmlType(const char* data, int data_len) = 0;
|
||||
|
||||
virtual HeaderType detectHeaderType(const char* name, int name_len) = 0;
|
||||
|
||||
virtual void start() = 0;
|
||||
virtual void set_transaction_time(const char* log_time) = 0;
|
||||
virtual void set_transaction_remote(const char* remote_addr, int remote_port) = 0;
|
||||
virtual void set_transaction_local(const char* local_addr, int local_port) = 0;
|
||||
|
||||
// Request
|
||||
virtual void set_method(const char* method) = 0;
|
||||
virtual void set_uri(const char* uri) = 0;
|
||||
virtual void start_request_hdrs() = 0;
|
||||
virtual void add_request_hdr(const char* name, int name_len, const char* value, int value_len) = 0;
|
||||
virtual void end_request_hdrs() = 0;
|
||||
virtual void start_request_body() = 0;
|
||||
virtual void add_request_body_chunk(const char* data, int data_len) = 0;
|
||||
virtual void end_request_body() = 0;
|
||||
virtual void end_request() = 0;
|
||||
|
||||
// Response
|
||||
virtual void start_response(int response_status, int http_version) = 0;
|
||||
virtual void start_response_hdrs() = 0;
|
||||
virtual void add_response_hdr(const char* name, int name_len, const char* value, int value_len) = 0;
|
||||
virtual void end_response_hdrs() = 0;
|
||||
virtual void start_response_body() = 0;
|
||||
virtual void add_response_body_chunk(const char* data, int data_len) = 0;
|
||||
virtual void end_response_body() = 0;
|
||||
virtual void end_response() = 0;
|
||||
|
||||
virtual void collectFoundPatterns() = 0;
|
||||
virtual ReportIS::Severity computeEventSeverityFromDecision() const = 0;
|
||||
virtual void finish() = 0;
|
||||
virtual Waf2TransactionFlags &getTransactionFlags() = 0;
|
||||
};
|
||||
69
components/security_apps/waap/include/i_waapConfig.h
Executable file
69
components/security_apps/waap/include/i_waapConfig.h
Executable file
@@ -0,0 +1,69 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../waap_clib/WaapOverride.h"
|
||||
#include "../waap_clib/WaapTrigger.h"
|
||||
#include "../waap_clib/TrustedSources.h"
|
||||
#include "../waap_clib/WaapParameters.h"
|
||||
#include "../waap_clib/WaapOpenRedirectPolicy.h"
|
||||
#include "../waap_clib/WaapErrorDisclosurePolicy.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,
|
||||
MEDIUM_BLOCKING_LEVEL,
|
||||
HIGH_BLOCKING_LEVEL
|
||||
};
|
||||
|
||||
enum class AttackMitigationMode
|
||||
{
|
||||
DISABLED = 0,
|
||||
LEARNING,
|
||||
PREVENT,
|
||||
UNKNOWN
|
||||
};
|
||||
class IWaapConfig {
|
||||
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_PracticeSubType() const = 0;
|
||||
virtual const std::string& get_RuleId() const = 0;
|
||||
virtual const std::string& get_RuleName() const = 0;
|
||||
virtual const bool& get_WebAttackMitigation() const = 0;
|
||||
virtual const std::string& get_WebAttackMitigationAction() const = 0;
|
||||
|
||||
virtual const std::shared_ptr<Waap::Override::Policy>& get_OverridePolicy() const = 0;
|
||||
virtual const std::shared_ptr<Waap::Trigger::Policy>& get_TriggerPolicy() const = 0;
|
||||
virtual const std::shared_ptr<Waap::TrustedSources::TrustedSourcesParameter>& get_TrustedSourcesPolicy() const = 0;
|
||||
virtual const std::shared_ptr<Waap::Parameters::WaapParameters>& get_WaapParametersPolicy() const = 0;
|
||||
virtual const std::shared_ptr<Waap::OpenRedirect::Policy>& get_OpenRedirectPolicy() const = 0;
|
||||
virtual const std::shared_ptr<Waap::ErrorDisclosure::Policy>& get_ErrorDisclosurePolicy() const = 0;
|
||||
virtual const std::shared_ptr<Waap::Csrf::Policy>& get_CsrfPolicy() const = 0;
|
||||
virtual const std::shared_ptr<Waap::RateLimiting::Policy>& get_RateLimitingPolicy() const = 0;
|
||||
virtual const std::shared_ptr<Waap::RateLimiting::Policy>& get_ErrorLimitingPolicy() const = 0;
|
||||
virtual const std::shared_ptr<Waap::SecurityHeaders::Policy>& get_SecurityHeadersPolicy() const = 0;
|
||||
virtual const std::shared_ptr<Waap::UserLimits::Policy>& get_UserLimitsPolicy() const = 0;
|
||||
|
||||
virtual void printMe(std::ostream& os) const = 0;
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user