mirror of
https://github.com/openappsec/openappsec.git
synced 2025-09-29 19:24:26 +03:00
First release of open-appsec source code
This commit is contained in:
@@ -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
|
Reference in New Issue
Block a user