First release of open-appsec source code

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

14
components/CMakeLists.txt Normal file
View File

@@ -0,0 +1,14 @@
add_subdirectory(report_messaging)
add_subdirectory(http_manager)
add_subdirectory(http_transaction_data)
add_subdirectory(generic_rulebase)
add_subdirectory(signal_handler)
add_subdirectory(gradual_deployment)
add_subdirectory(packet)
add_subdirectory(pending_key)
add_subdirectory(messaging_downloader)
add_subdirectory(health_check_manager)
add_subdirectory(utils)
add_subdirectory(attachment-intakers)
add_subdirectory(security_apps)

View File

@@ -0,0 +1,2 @@
add_subdirectory(nginx_attachment)
add_subdirectory(attachment_registrator)

View File

@@ -0,0 +1 @@
add_library(attachment_registrator attachment_registrator.cc)

View File

@@ -0,0 +1,470 @@
// 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 "attachment_registrator.h"
#include <iostream>
#include <fstream>
#include <map>
#include <sstream>
#include <fstream>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/un.h>
#include <climits>
#include <unordered_map>
#include <unistd.h>
#include <utility>
#include "common.h"
#include "config.h"
#include "singleton.h"
#include "i_mainloop.h"
#include "buffer.h"
#include "enum_array.h"
#include "nginx_attachment_common.h"
USE_DEBUG_FLAG(D_ATTACHMENT_REGISTRATION);
using namespace std;
class AttachmentRegistrator::Impl
{
public:
void
init()
{
i_socket = Singleton::Consume<I_Socket>::by<AttachmentRegistrator>();
Singleton::Consume<I_MainLoop>::by<AttachmentRegistrator>()->addOneTimeRoutine(
I_MainLoop::RoutineType::System,
[this] ()
{
while(!initSocket()) {
Singleton::Consume<I_MainLoop>::by<AttachmentRegistrator>()->yield(chrono::seconds(1));
}
},
"Initialize attachment registration IPC"
);
uint expiration_timeout = getProfileAgentSettingWithDefault<uint>(
300, "attachmentRegistrator.expirationCheckSeconds"
);
Singleton::Consume<I_MainLoop>::by<AttachmentRegistrator>()->addRecurringRoutine(
I_MainLoop::RoutineType::Timer,
chrono::seconds(expiration_timeout),
[this] () { handleExpiration(); },
"Attachment's expiration handler",
true
);
}
void
fini()
{
if (server_sock > 0) {
i_socket->closeSocket(server_sock);
server_sock = -1;
}
if (shared_registration_path != "") unlink(shared_registration_path.c_str());
}
private:
bool
registerAttachmentProcess(
const uint8_t &uid,
const string &family_id,
const uint8_t num_of_members,
const AttachmentType type)
{
registered_attachments[family_id] = vector<bool>(num_of_members, true);
const int cmd_tmout = 900;
I_ShellCmd *shell_cmd = Singleton::Consume<I_ShellCmd>::by<AttachmentRegistrator>();
Maybe<string> registration_res = shell_cmd->getExecOutput(
genRegCommand(family_id, num_of_members, type),
cmd_tmout
);
if (!registration_res.ok()) {
dbgWarning(D_ATTACHMENT_REGISTRATION)
<< "Failed to register attachment."
<< "Attachment Type: "
<< static_cast<int>(type)
<< ", Attachment id: "
<< uid
<<", Family id: "
<< family_id
<< ", Total number of instances: "
<< num_of_members;
return false;
}
return true;
}
void
replyWithRelevantHandler(
I_Socket::socketFd socket,
const uint8_t &uid,
const string &family_id,
const AttachmentType type)
{
string handler_path = genHandlerPath(uid, family_id, type);
uint8_t path_size = handler_path.size();
vector<char> path_size_data(reinterpret_cast<char *>(&path_size), reinterpret_cast<char *>(&path_size) + 1);
if (!i_socket->writeData(socket, path_size_data)) {
dbgWarning(D_ATTACHMENT_REGISTRATION) << "Failed to send handler path size to attachment";
return;
}
dbgDebug(D_ATTACHMENT_REGISTRATION)
<< "Successfully sent handler path size to attachment. Size: "
<< to_string(path_size);
vector<char> path_data(handler_path.data(), handler_path.data() + handler_path.size());
if (!i_socket->writeData(socket, path_data)) {
dbgWarning(D_ATTACHMENT_REGISTRATION)
<< "Failed to send handler path data to attachment. Path: "
<< handler_path;
return;
}
dbgDebug(D_ATTACHMENT_REGISTRATION)
<< "Successfully sent handler path data to attachment. Path: "
<< handler_path;
}
string
genHandlerPath(const uint8_t &uid, const string &family_id, const AttachmentType type) const
{
static const string handler_path_format = "/dev/shm/check-point/cp-nano-";
stringstream handler_path;
handler_path << handler_path_format;
switch(type) {
case (AttachmentType::NGINX_ATT_ID): {
handler_path << "http-transaction-handler-";
break;
}
default:
dbgAssert(false) << "Unsupported Attachment " << static_cast<int>(type);
}
if (!family_id.empty()) handler_path << family_id << "_";
handler_path << to_string(uid);
return handler_path.str();
}
string
genRegCommand(const string &family_id, const uint num_of_members, const AttachmentType type) const
{
dbgAssert(num_of_members > 0) << "Failed to generate a registration command for an empty group of attachments";
static const string registration_format = "/etc/cp/watchdog/cp-nano-watchdog --register ";
stringstream registration_command;
registration_command<< registration_format;
switch(type) {
case (AttachmentType::NGINX_ATT_ID): {
registration_command << "/etc/cp/HttpTransactionHandler/cp-nano-http-transaction-handler";
break;
}
default:
dbgAssert(false) << "Unsupported Attachment " << static_cast<int>(type);
}
if (!family_id.empty()) registration_command << " --family " << family_id;
registration_command << " --count " << to_string(num_of_members);
return registration_command.str();
}
bool
initSocket()
{
shared_registration_path = getConfigurationWithDefault<string>(
"/dev/shm/check-point/cp-nano-attachment-registration",
"Attachment Registration",
"Registration IPC Path"
);
size_t last_slash_idx = shared_registration_path.find_last_of("/");
string directory_path = shared_registration_path.substr(0, last_slash_idx);
mkdir(directory_path.c_str(), 0777);
if (server_sock < 0) {
server_sock = getNewSocket(shared_registration_path);
if (server_sock < 0) {
dbgWarning(D_ATTACHMENT_REGISTRATION)
<< "Failed to create server socket. Path: "
<< shared_registration_path;
return false;
}
Singleton::Consume<I_MainLoop>::by<AttachmentRegistrator>()->addFileRoutine(
I_MainLoop::RoutineType::RealTime,
server_sock,
[this] () { handleAttachmentRegistration(); },
"Attachment's registration handler",
true
);
}
string shared_expiration_path = getConfigurationWithDefault<string>(
SHARED_KEEP_ALIVE_PATH,
"Attachment Registration",
"Registration IPC Path"
);
if (keep_alive_sock < 0) {
keep_alive_sock = getNewSocket(shared_expiration_path);
if (keep_alive_sock < 0) {
dbgWarning(D_ATTACHMENT_REGISTRATION) << "Failed to create keep-alive socket";
return false;
}
Singleton::Consume<I_MainLoop>::by<AttachmentRegistrator>()->addFileRoutine(
I_MainLoop::RoutineType::System,
keep_alive_sock,
[this] () { handleKeepAlives(); },
"Attachment keep alive registration",
true
);
}
return true;
}
I_Socket::socketFd
getNewSocket(const string &path)
{
Maybe<I_Socket::socketFd> new_socket = i_socket->genSocket(
I_Socket::SocketType::UNIX,
false,
true,
path
);
if (!new_socket.ok()) {
dbgWarning(D_ATTACHMENT_REGISTRATION) << "Failed to open a socket. Error: " << new_socket.getErr();
return -1;
}
dbgAssert(new_socket.unpack() > 0) << "Generated socket is OK yet negative";
return new_socket.unpack();
}
void
handleKeepAlives()
{
Maybe<I_Socket::socketFd> accepted_socket = i_socket->acceptSocket(keep_alive_sock, false);
if (!accepted_socket.ok()) {
dbgWarning(D_ATTACHMENT_REGISTRATION)
<< "Failed to accept new keep-alive request socket: "
<< accepted_socket.getErr();
return;
}
I_Socket::socketFd client_socket = accepted_socket.unpack();
dbgAssert(client_socket > 0) << "Generated client socket is OK yet negative";
auto close_socket_on_exit = make_scope_exit([&]() { i_socket->closeSocket(client_socket); });
Maybe<uint8_t> attachment_id = readNumericParam(client_socket);
if (!attachment_id.ok()) {
dbgWarning(D_ATTACHMENT_REGISTRATION) << "Failed to register new attachment: " << attachment_id.getErr();
return;
}
Maybe<string> family_id = readStringParam(client_socket);
if (!family_id.ok()) {
dbgWarning(D_ATTACHMENT_REGISTRATION) << "Failed to register new attachment: " << family_id.getErr();
return;
}
if (family_id.unpack() == "") return;
auto family_members = registered_attachments.find(family_id.unpack());
if (family_members == registered_attachments.end()) {
dbgWarning(D_ATTACHMENT_REGISTRATION)
<< "Adding new unregistered family. Family ID: "
<< family_id.unpack();
registered_attachments[family_id.unpack()] = vector<bool>(attachment_id.unpack() + 1, true);
return;
}
if (family_members->second.size() <= attachment_id.unpack()) {
dbgWarning(D_ATTACHMENT_REGISTRATION)
<< "Adding new non-monitored family members. Family ID: "
<< family_id.unpack()
<< ", Instance ID:"
<< attachment_id.unpack();
registered_attachments[family_id.unpack()] = vector<bool>(attachment_id.unpack() + 1, true);
return;
}
family_members->second[attachment_id.unpack()] = true;
}
void
handleExpiration()
{
I_ShellCmd *shell_cmd = Singleton::Consume<I_ShellCmd>::by<AttachmentRegistrator>();
vector<string> deleted_families;
for (pair<const string, vector<bool, allocator<bool>>> &family : registered_attachments) {
const string &family_id = family.first;
if (family_id == "") continue;
bool is_family_inactive = true;
vector<bool> &family_members = family.second;
for (const bool member : family_members) {
if (member == true) is_family_inactive = false;
}
if (is_family_inactive) {
static const string unregister_format = "/etc/cp/watchdog/cp-nano-watchdog --un-register ";
stringstream unregister_command;
unregister_command << unregister_format;
unregister_command << "/etc/cp/HttpTransactionHandler/cp-nano-http-transaction-handler";
unregister_command << " --family " << family_id;
Maybe<string> res = shell_cmd->getExecOutput(unregister_command.str());
if (!res.ok()) {
dbgWarning(D_ATTACHMENT_REGISTRATION)
<< "Failed to un-register attachment. Family id: "
<< family_id;
} else {
deleted_families.push_back(family_id);
}
} else {
fill(family_members.begin(), family_members.end(), false);
}
}
for (const string &family : deleted_families) {
registered_attachments.erase(family);
dbgWarning(D_ATTACHMENT_REGISTRATION)
<< "Successfully un-registered attachments family. Family id: "
<< family;
}
}
void
handleAttachmentRegistration()
{
Maybe<I_Socket::socketFd> accepted_socket = i_socket->acceptSocket(server_sock, false);
if (!accepted_socket.ok()) {
dbgWarning(D_ATTACHMENT_REGISTRATION)
<< "Failed to accept a new client socket: "
<< accepted_socket.getErr();
return;
}
I_Socket::socketFd client_socket = accepted_socket.unpack();
dbgAssert(client_socket > 0) << "Generated client socket is OK yet negative";
auto close_socket_on_exit = make_scope_exit([&]() { i_socket->closeSocket(client_socket); });
Maybe<AttachmentType> attachment_type = readAttachmentType(client_socket);
if (!attachment_type.ok()) {
dbgWarning(D_ATTACHMENT_REGISTRATION)
<< "Failed to register a new attachment: "
<< attachment_type.getErr();
return;
}
Maybe<uint8_t> attachment_id = readNumericParam(client_socket);
if (!attachment_id.ok()) {
dbgWarning(D_ATTACHMENT_REGISTRATION) << "Failed to register a new attachment: " << attachment_id.getErr();
return;
}
Maybe<uint8_t> instances_count = readNumericParam(client_socket);
if (!instances_count.ok()) {
dbgWarning(D_ATTACHMENT_REGISTRATION)
<< "Failed to register a new attachment: "
<< instances_count.getErr();
return;
}
Maybe<string> family_id = readStringParam(client_socket);
if (!family_id.ok()) {
dbgWarning(D_ATTACHMENT_REGISTRATION) << "Failed to register a new attachment: " << family_id.getErr();
return;
}
if (!registerAttachmentProcess(*attachment_id, *family_id, *instances_count, *attachment_type)) {
return;
}
replyWithRelevantHandler(client_socket, *attachment_id, *family_id, *attachment_type);
}
Maybe<uint8_t>
readNumericParam(I_Socket::socketFd socket)
{
Maybe<vector<char>> param_to_read = i_socket->receiveData(socket, sizeof(uint8_t));
if (!param_to_read.ok()) {
dbgWarning(D_ATTACHMENT_REGISTRATION) << "Failed to read param: " << param_to_read.getErr();
return genError("Failed to read numeric parameter");
}
return *reinterpret_cast<const uint8_t *>(param_to_read.unpack().data());
}
Maybe<AttachmentType>
readAttachmentType(I_Socket::socketFd socket)
{
Maybe<uint8_t> attachment_type = readNumericParam(socket);
if (!attachment_type.ok()) return attachment_type.passErr();
dbgTrace(D_ATTACHMENT_REGISTRATION)
<< "Successfully received attachment type. Attachment type value: "
<< static_cast<int>(*attachment_type);
return convertToEnum<AttachmentType>(*attachment_type);
}
Maybe<string>
readStringParam(I_Socket::socketFd socket)
{
Maybe<uint8_t> param_size = readNumericParam(socket);
if (!param_size.ok()) return param_size.passErr();
dbgTrace(D_ATTACHMENT_REGISTRATION)
<< "Successfully received string size. Size: "
<< static_cast<int>(*param_size);
Maybe<vector<char>> param_to_read = i_socket->receiveData(socket, param_size.unpack());
return string(param_to_read.unpack().begin(), param_to_read.unpack().end());
}
I_Socket::socketFd server_sock = -1;
I_Socket::socketFd keep_alive_sock = -1;
I_Socket *i_socket = nullptr;
map<string, vector<bool>> registered_attachments;
string shared_registration_path;
};
AttachmentRegistrator::AttachmentRegistrator() : Component("AttachmentRegistrator"), pimpl(make_unique<Impl>()) {}
AttachmentRegistrator::~AttachmentRegistrator() {}
void AttachmentRegistrator::init() { pimpl->init(); }
void AttachmentRegistrator::fini() { pimpl->fini(); }
void
AttachmentRegistrator::preload()
{
registerExpectedConfiguration<string>("Attachment Registration", "Registration IPC Path");
}

View File

@@ -0,0 +1,5 @@
add_definitions(-DUSERSPACE)
add_library(nginx_attachment nginx_attachment.cc nginx_attachment_config.cc nginx_attachment_opaque.cc nginx_parser.cc user_identifiers_config.cc nginx_intaker_metric.cc nginx_attachment_metric.cc cidrs_data.cc)
target_link_libraries(nginx_attachment http_configuration http_transaction_data connkey table buffers -lshmem_ipc)

View File

@@ -0,0 +1,128 @@
#include "cidrs_data.h"
#include "log_generator.h"
using namespace std;
USE_DEBUG_FLAG(D_NGINX_ATTACHMENT_PARSER);
bool
CIDRSData::matchCidr(const in_addr &address, const in_addr &network) const
{
if (network_bits == 0) {
// C99 6.5.7 (3): u32 << 32 is undefined behaviour
return true;
}
return !((address.s_addr ^ network.s_addr) & htonl(0xFFFFFFFFu << (32 - network_bits)));
}
bool
CIDRSData::matchCidr(const in6_addr &address, const in6_addr &network) const
{
#ifdef __linux__
const uint32_t *a = address.s6_addr32;
const uint32_t *n = network.s6_addr32;
#else
const uint32_t *a = address.__u6_addr.__u6_addr32;
const uint32_t *n = network.__u6_addr.__u6_addr32;
#endif
int bits_whole, bits_incomplete;
bits_whole = network_bits >> 5; // number of whole u32
bits_incomplete = network_bits & 0x1F; // number of bits in incomplete u32
if (bits_whole) {
if (memcmp(a, n, bits_whole << 2)) {
return false;
}
}
if (bits_incomplete) {
uint32_t mask = htonl((0xFFFFFFFFu) << (32 - bits_incomplete));
if ((a[bits_whole] ^ n[bits_whole]) & mask) {
return false;
}
}
return true;
}
CIDRSData::CIDRSData(const string &str_cidr)
{
size_t processed_bits = 0;
size_t pos = str_cidr.find_last_of('/');
// get ip from targetCidr
string str_prefix = pos != string::npos ? str_cidr.substr(0, pos) : str_cidr;
// get subnet mask from targetCidr or calculate it based on ipv4 / ipv6
string str_suffix;
if (pos != string::npos) {
str_suffix = str_cidr.substr(pos + 1);
} else if (str_cidr.find(':') == string::npos) {
str_suffix = "32";
} else {
str_suffix = "128";
}
int bits = -1;
try {
bits = stoi(str_suffix, &processed_bits);
network_bits = (uint8_t)bits;
// convert int to uint8_t
} catch (...) {
dbgWarning(D_NGINX_ATTACHMENT_PARSER)
<< "Failed to convert CIDR number of bits from string to int"
<< str_cidr;
return;
}
// check if CIDR is valid
if (processed_bits != str_suffix.length() || bits > 128 || bits < 0) {
dbgWarning(D_NGINX_ATTACHMENT_PARSER)
<< "Failed to convert CIDR number of bits from string to int (out of range)."
<< str_cidr;
return;
}
if (IPAddr::isValidIPAddr(str_prefix)) {
ip_addr = IPAddr::createIPAddr(str_prefix).unpack();
} else {
dbgDebug(D_NGINX_ATTACHMENT_PARSER) << "Failed to convert CIDR number of bits from string to int";
return;
}
dbgDebug(D_NGINX_ATTACHMENT_PARSER) << "successfully created cidr from the following string: " << str_cidr;
valid_cidr = true;
}
bool
CIDRSData::contains(const string &source_ip) const
{
if(!valid_cidr) {
dbgDebug(D_NGINX_ATTACHMENT_PARSER) << "Invalid CIDR.";
return false;
}
// check from which type the target ip and check if ip belongs to is mask ip
//convert source_ip to ip v4 or v6.
switch (ip_addr.getType()) {
case IPType::V4: {
struct in_addr source_inaddr;
if (inet_pton(AF_INET, source_ip.c_str(), &source_inaddr) == 1) {
return matchCidr(source_inaddr, ip_addr.getIPv4());
}
break;
}
case IPType::V6: {
struct in6_addr source_inaddr6;
if (inet_pton(AF_INET6, source_ip.c_str(), &source_inaddr6) == 1) {
return matchCidr(source_inaddr6, ip_addr.getIPv6());
}
break;
}
default: {
dbgWarning(D_NGINX_ATTACHMENT_PARSER) << "Unexpected ip type";
}
}
return false;
}

View File

@@ -0,0 +1,28 @@
#ifndef __CIDRS_DATA_H__
#define __CIDRS_DATA_H__
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>
#include "maybe_res.h"
#include "connkey.h"
class CIDRSData
{
public:
CIDRSData(const std::string &str_cidr);
bool contains(const std::string &source_ip) const;
private:
bool matchCidr(const in_addr &address, const in_addr &net) const;
bool matchCidr(const in6_addr &address, const in6_addr &network) const;
IPAddr ip_addr;
uint8_t network_bits;
bool valid_cidr = false;
};
#endif // __CIDRS_DATA_H__

View File

@@ -0,0 +1,145 @@
// 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 "intentional_failure.h"
#include <string>
#include <unistd.h>
#include "config.h"
#include "debug.h"
using namespace std;
USE_DEBUG_FLAG(D_NGINX_ATTACHMENT);
IntentionalFailureHandler::FailureType
getFailureTypeFromString(const string &failure)
{
if (failure == "create socket") return IntentionalFailureHandler::FailureType::CreateSocket;
if (failure == "accept socket") return IntentionalFailureHandler::FailureType::AcceptSocket;
if (failure == "initialize connection channel")
return IntentionalFailureHandler::FailureType::InitializeConnectionChannel;
if (failure == "write to socket") return IntentionalFailureHandler::FailureType::WriteDataToSocket;
if (failure == "read from socket") return IntentionalFailureHandler::FailureType::ReceiveDataFromSocket;
if (failure == "parse response") return IntentionalFailureHandler::FailureType::ParsingResponse;
if (failure == "get data from attachment") return IntentionalFailureHandler::FailureType::GetDataFromAttchment;
if (failure == "register attachment") return IntentionalFailureHandler::FailureType::RegisterAttchment;
if (failure == "get instance id") return IntentionalFailureHandler::FailureType::GetInstanceID;
if (failure != "") {
dbgInfo(D_NGINX_ATTACHMENT) << "Ignoring unknown intentional failure type:" << failure;
}
return IntentionalFailureHandler::FailureType::None;
}
void
IntentionalFailureHandler::RegisterIntentionalFailure()
{
is_failure_enabled = getConfigurationWithDefault<bool>(
false, "HTTP manager", "Enable intentional failure mode"
);
string failure_type_str = getConfigurationWithDefault<string>("", "HTTP manager", "Intentional failure type");
failure_type = getFailureTypeFromString(failure_type_str);
if (failure_type == FailureType::None) is_failure_enabled = false;
allow_count = getConfigurationWithDefault<int>(0, "HTTP manager", "Intentional failure allow times");
fail_count = getConfigurationWithDefault<int>(-1, "HTTP manager", "Intentional failure limit");
is_limited = fail_count > 0;
is_delay_enabled = getConfigurationWithDefault<bool>(
false, "HTTP manager", "Enable intentional delay mode"
);
string delay_failure_type_str = getConfigurationWithDefault<string>(
"", "HTTP manager", "Intentional delay failure type"
);
delay_failure_type = getFailureTypeFromString(delay_failure_type_str);
delay_amount = chrono::microseconds(
getConfigurationWithDefault<int>(-1, "HTTP manager", "Intentional delay amount")
);
if (delay_failure_type == FailureType::None || delay_amount <= chrono::microseconds(0)) is_delay_enabled = false;
if (is_failure_enabled) {
dbgInfo(D_NGINX_ATTACHMENT) << "Registered Intentional failure. Type: " << failure_type_str
<< ", will allow first " << to_string(allow_count) << " actions"
<< ", fail limit: " << (is_limited ? to_string(fail_count) : "unlimited");
}
if (is_delay_enabled) {
dbgInfo(D_NGINX_ATTACHMENT) << "Registered Intentional delay. Type: " << delay_failure_type_str
<< ", amount: " << delay_amount.count() << " microseconds";
}
}
void
IntentionalFailureHandler::init()
{
RegisterIntentionalFailure();
registerConfigLoadCb([this]() { RegisterIntentionalFailure(); });
if (!is_failure_enabled && !is_delay_enabled) {
dbgInfo(D_NGINX_ATTACHMENT) << "Initialized Intentional failure. No failure/delay was specified";
}
}
bool
IntentionalFailureHandler::shouldFail(
bool was_originaly_successful,
IntentionalFailureHandler::FailureType failure,
bool *failed_on_purpose
)
{
*failed_on_purpose = false;
if (is_failure_enabled && failure_type == failure) {
if (allow_count > 0) {
allow_count --;
dbgInfo(D_NGINX_ATTACHMENT) << "Intentional failure: allowed action, remaining tries to be allowed: "
<< to_string(allow_count);
return !was_originaly_successful;
}
if (is_limited) {
if (fail_count <= 0) return !was_originaly_successful;
fail_count --;
}
dbgInfo(D_NGINX_ATTACHMENT) << "Intentional failure was activated, remaining failures: "
<< (is_limited ? to_string(fail_count) : "unlimited");
*failed_on_purpose = true;
return true;
}
return !was_originaly_successful;
}
void
IntentionalFailureHandler::delayIfNeeded(IntentionalFailureHandler::FailureType failure)
{
if (is_delay_enabled && delay_failure_type == failure) {
dbgInfo(D_NGINX_ATTACHMENT) << "Intentional delay was activated (" << delay_amount.count() << " microseconds)";
usleep(delay_amount.count());
}
}
void
IntentionalFailureHandler::preload()
{
registerExpectedConfiguration<bool>("HTTP manager", "Enable intentional failure mode");
registerExpectedConfiguration<string>("HTTP manager", "Intentional failure type");
registerExpectedConfiguration<int>("HTTP manager", "Intentional failure limit");
registerExpectedConfiguration<int>("HTTP manager", "Intentional failure allow times");
registerExpectedConfiguration<bool>("HTTP manager", "Enable intentional delay mode");
registerExpectedConfiguration<string>("HTTP manager", "Intentional delay failure type");
registerExpectedConfiguration<int>("HTTP manager", "Intentional delay amount");
}

View File

@@ -0,0 +1,56 @@
// 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 __INTENTIONAL_FAILURE__
#define __INTENTIONAL_FAILURE__
#include <chrono>
class IntentionalFailureHandler
{
public:
enum class FailureType {
None,
CreateSocket,
AcceptSocket,
InitializeConnectionChannel,
WriteDataToSocket,
ReceiveDataFromSocket,
ParsingResponse,
GetDataFromAttchment,
RegisterAttchment,
GetInstanceID,
COUNT
};
void init();
bool shouldFail(bool was_originaly_successful, FailureType failure, bool *failed_on_purpose);
void delayIfNeeded(FailureType failure);
void preload();
private:
void RegisterIntentionalFailure();
FailureType failure_type;
bool is_failure_enabled;
bool is_limited;
int fail_count;
int allow_count;
FailureType delay_failure_type;
bool is_delay_enabled;
std::chrono::microseconds delay_amount;
};
#endif // __INTENTIONAL_FAILURE__

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,331 @@
// 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 "nginx_attachment_config.h"
#include <stdlib.h>
#include "nginx_attachment.h"
#include "config.h"
#include "singleton.h"
#include "i_gradual_deployment.h"
#include "debug.h"
USE_DEBUG_FLAG(D_NGINX_ATTACHMENT);
using namespace std;
using DebugLevel = ngx_http_cp_debug_level_e;
void
HttpAttachmentConfig::init()
{
setDebugLevel();
setGradualDeploymentIPs();
setWebTriggerConf();
setStaticResourcesPath();
setFailOpenMode();
setFailOpenTimeout();
setFailOpenWaitMode();
setSessionsPerMinuteLimitVerdict();
setMaxSessionsPerMinute();
setNumOfNginxIpcElements();
setDebugByContextValues();
setKeepAliveIntervalMsec();
}
bool
HttpAttachmentConfig::operator==(const HttpAttachmentConfig &other) const
{
return
web_trigger_conf == other.web_trigger_conf &&
conf_data == other.conf_data;
}
void
HttpAttachmentConfig::save(cereal::JSONOutputArchive &out_ar) const
{
conf_data.save(out_ar);
}
template <typename Conf, typename ...Strings>
static Conf
getAttachmentConf(const Conf &default_val, const string &profile_conf, const Strings & ...conf)
{
const Conf &profile_settings = getProfileAgentSettingWithDefault<Conf>(default_val, profile_conf);
return getConfigurationWithDefault<Conf>(profile_settings, conf...);
}
void
HttpAttachmentConfig::setGradualDeploymentIPs()
{
auto i_gradual_deployment = Singleton::Consume<I_GradualDeployment>::by<NginxAttachment>();
conf_data.setExcludeSources(i_gradual_deployment->getPolicy(I_GradualDeployment::AttachmentType::NGINX));
}
void
HttpAttachmentConfig::setWebTriggerConf()
{
web_trigger_conf = getConfigurationWithDefault<WebTriggerConf>(
WebTriggerConf::default_trigger_conf,
"HTTP manager",
"Web trigger conf"
);
}
void
HttpAttachmentConfig::setDebugLevel() {
string debug_level = getAttachmentConf<string>(
"info",
"agent.debug.flag.nginxModule",
"HTTP manager",
"Attachment debug level"
);
debug_level[0] = toupper(debug_level[0]);
if (debug_level == "Trace") {
conf_data.setNumericalValue("dbg_level", (unsigned int)DebugLevel::DBG_LEVEL_TRACE);
} else if (debug_level == "Debug") {
conf_data.setNumericalValue("dbg_level", (unsigned int)DebugLevel::DBG_LEVEL_DEBUG);
} else if (debug_level == "Info") {
conf_data.setNumericalValue("dbg_level", (unsigned int)DebugLevel::DBG_LEVEL_INFO);
} else if (debug_level == "Warning") {
conf_data.setNumericalValue("dbg_level", (unsigned int)DebugLevel::DBG_LEVEL_WARNING);
} else if (debug_level == "Error") {
conf_data.setNumericalValue("dbg_level", (unsigned int)DebugLevel::DBG_LEVEL_ERROR);
} else {
dbgWarning(D_NGINX_ATTACHMENT)
<< "Debug level \""
<< debug_level
<< "\" is not valid. using default level \"warning\"";
conf_data.setNumericalValue("dbg_level", (unsigned int)DebugLevel::DBG_LEVEL_INFO);
}
}
void
HttpAttachmentConfig::setFailOpenMode()
{
bool is_fail_open_mode_enabled = getAttachmentConf<bool>(
true,
"agent.failOpenState.nginxModule",
"HTTP manager",
"Fail Open Mode state"
);
dbgTrace(D_NGINX_ATTACHMENT)
<< "Attachment failure mode is: "
<< (is_fail_open_mode_enabled ? "Enabled" : "Disabled");
conf_data.setNumericalValue("is_fail_open_mode_enabled", is_fail_open_mode_enabled);
}
void
HttpAttachmentConfig::setFailOpenTimeout()
{
conf_data.setNumericalValue("fail_open_timeout", getAttachmentConf<uint>(
50,
"agent.failOpenTimeout.nginxModule",
"HTTP manager",
"Fail Open timeout msec"
));
conf_data.setNumericalValue("fail_open_hold_timeout", getAttachmentConf<uint>(
150,
"agent.failOpenWaitTimeout.nginxModule",
"HTTP manager",
"Fail Open wait timeout msec"
));
conf_data.setNumericalValue("res_proccessing_timeout_msec", getAttachmentConf<uint>(
3000,
"agent.resProccessingTimeout.nginxModule",
"HTTP manager",
"NGINX response processing timeout msec"
));
conf_data.setNumericalValue("req_proccessing_timeout_msec", getAttachmentConf<uint>(
3000,
"agent.reqProccessingTimeout.nginxModule",
"HTTP manager",
"NGINX request processing timeout msec"
));
conf_data.setNumericalValue("registration_thread_timeout_msec", getAttachmentConf<uint>(
100,
"agent.registrationThreadTimeout.nginxModule",
"HTTP manager",
"NGINX registration thread timeout msec"
));
conf_data.setNumericalValue("req_header_thread_timeout_msec", getAttachmentConf<uint>(
100,
"agent.reqHeaderThreadTimeout.nginxModule",
"HTTP manager",
"NGINX request header thread timeout msec"
));
conf_data.setNumericalValue("req_body_thread_timeout_msec", getAttachmentConf<uint>(
150,
"agent.reqBodyThreadTimeout.nginxModule",
"HTTP manager",
"NGINX request body thread timeout msec"
));
conf_data.setNumericalValue("res_header_thread_timeout_msec", getAttachmentConf<uint>(
100,
"agent.resHeaderThreadTimeout.nginxModule",
"HTTP manager",
"NGINX response header thread timeout msec"
));
conf_data.setNumericalValue("res_body_thread_timeout_msec", getAttachmentConf<uint>(
150,
"agent.resBodyThreadTimeout.nginxModule",
"HTTP manager",
"NGINX response body thread timeout msec"
));
conf_data.setNumericalValue("waiting_for_verdict_thread_timeout_msec", getAttachmentConf<uint>(
150,
"agent.waitThreadTimeout.nginxModule",
"HTTP manager",
"NGINX wait thread timeout msec"
));
uint inspection_mode = getAttachmentConf<uint>(
static_cast<uint>(ngx_http_inspection_mode_e::NON_BLOCKING_THREAD),
"agent.inspectionMode.nginxModule",
"HTTP manager",
"NGINX inspection mode"
);
if (inspection_mode >= ngx_http_inspection_mode_e::INSPECTION_MODE_COUNT) {
inspection_mode = ngx_http_inspection_mode_e::NON_BLOCKING_THREAD;
}
conf_data.setNumericalValue("nginx_inspection_mode", inspection_mode);
}
void
HttpAttachmentConfig::setFailOpenWaitMode()
{
bool is_fail_open_mode_hold_enabled = getAttachmentConf<bool>(
true,
"agent.failOpenWaitState.nginxModule",
"HTTP manager",
"Fail Open Mode state"
);
dbgTrace(D_NGINX_ATTACHMENT)
<< "Attachment waiting failure mode is: "
<< (is_fail_open_mode_hold_enabled ? "Enabled" : "Disabled");
conf_data.setNumericalValue("is_fail_open_mode_hold_enabled", is_fail_open_mode_hold_enabled);
}
void
HttpAttachmentConfig::setSessionsPerMinuteLimitVerdict()
{
string sessions_per_minute_limit_verdict = getAttachmentConf<string>(
"Accept",
"agent.sessionsPerMinuteLimitVerdict.nginxModule",
"HTTP manager",
"Sessions Per Minute Limit Verdict"
);
dbgTrace(D_NGINX_ATTACHMENT)
<< "Attachment sessions per minute limit verdict is: "
<< sessions_per_minute_limit_verdict;
conf_data.setStringValue("sessions_per_minute_limit_verdict", sessions_per_minute_limit_verdict);
}
void
HttpAttachmentConfig::setMaxSessionsPerMinute()
{
uint max_sessions_per_minute = getAttachmentConf<uint>(
0,
"agent.maxSessionsPerMinute.nginxModule",
"HTTP manager",
"Max Sessions Per Minute"
);
dbgTrace(D_NGINX_ATTACHMENT)
<< "Attachment max sessions per minute is: "
<< max_sessions_per_minute;
conf_data.setNumericalValue("max_sessions_per_minute", max_sessions_per_minute);
}
void
HttpAttachmentConfig::setNumOfNginxIpcElements()
{
uint num_of_nginx_ipc_elements = getProfileAgentSettingWithDefault<uint>(
NUM_OF_NGINX_IPC_ELEMENTS, "nginxAttachment.numOfNginxIpcElements"
);
dbgTrace(D_NGINX_ATTACHMENT)
<< "Number of NGINX IPC elements: "
<< num_of_nginx_ipc_elements;
conf_data.setNumericalValue("num_of_nginx_ipc_elements", num_of_nginx_ipc_elements);
}
void
HttpAttachmentConfig::setKeepAliveIntervalMsec()
{
uint keep_alive_interval_msec = getProfileAgentSettingWithDefault<uint>(
300, "attachmentRegistrator.expirationCheckSeconds"
);
keep_alive_interval_msec = (keep_alive_interval_msec * 1000) / 2;
dbgDebug(D_NGINX_ATTACHMENT)
<< "Interval keeps alives size: "
<< keep_alive_interval_msec << " msec";
conf_data.setNumericalValue("keep_alive_interval_msec", keep_alive_interval_msec);
}
void
HttpAttachmentConfig::setStaticResourcesPath()
{
string static_resources_path = getConfigurationWithDefault<string>(
DEFAULT_STATIC_RESOURCES_PATH,
"HTTP manager",
"Static resources path"
);
dbgDebug(D_NGINX_ATTACHMENT) << "Static resources path is : " << static_resources_path;
conf_data.setStringValue("static_resources_path", static_resources_path);
}
void
HttpAttachmentConfig::setDebugByContextValues()
{
DebugConfig new_ctx_cfg;
auto maybe_ctx_config = getSetting<DebugConfig>("HTTP manager", "debug context");
if(!maybe_ctx_config.ok()) {
dbgDebug(D_NGINX_ATTACHMENT) << "Failed to set context values. Setting default values";
conf_data.setDebugContext(new_ctx_cfg);
return;
}
new_ctx_cfg = maybe_ctx_config.unpack();
conf_data.setDebugContext(new_ctx_cfg);
dbgDebug(D_NGINX_ATTACHMENT)
<< "Setting context values : "
<< "client_ip: "
<< new_ctx_cfg.client
<< ", listening_ip: "
<< new_ctx_cfg.server
<< ", uri_prefix: "
<< new_ctx_cfg.uri
<< ", hostname: "
<< new_ctx_cfg.host
<< ", http_method: "
<< new_ctx_cfg.method
<< ", listening_port: "
<< new_ctx_cfg.port;
}

View File

@@ -0,0 +1,77 @@
// 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 __NGINX_ATTACHMENT_CONFIG_H__
#define __NGINX_ATTACHMENT_CONFIG_H__
#include <string>
#include <vector>
#include "nginx_attachment_util.h"
#include "cereal/archives/json.hpp"
#include "generic_rulebase/triggers_config.h"
#include "http_configuration.h"
class HttpAttachmentConfig
{
public:
void init();
bool operator==(const HttpAttachmentConfig &other) const;
void save(cereal::JSONOutputArchive &out_ar) const;
unsigned int getDebugLevel() const { return conf_data.getNumericalValue("dbg_level"); }
bool getIsFailOpenModeEnabled() const { return conf_data.getNumericalValue("is_fail_open_mode_enabled"); }
bool
getSessionsPerMinuteLimitVerdict() const
{
return conf_data.getNumericalValue("sessions_per_minute_limit_verdict");
}
unsigned int getMaxSessionsPerMinute() const { return conf_data.getNumericalValue("max_sessions_per_minute"); }
unsigned int getNumOfNginxElements() const { return conf_data.getNumericalValue("num_of_nginx_ipc_elements"); }
unsigned int getKeepAliveIntervalMsec() const { return conf_data.getNumericalValue("keep_alive_interval_msec"); }
private:
void setGradualDeploymentIPs();
void setWebTriggerConf();
void setDebugLevel();
void setFailOpenMode();
void setFailOpenTimeout();
void setFailOpenWaitMode();
void setSessionsPerMinuteLimitVerdict();
void setMaxSessionsPerMinute();
void setNumOfNginxIpcElements();
void setKeepAliveIntervalMsec();
void setStaticResourcesPath();
void setDebugByContextValues();
WebTriggerConf web_trigger_conf;
HttpAttachmentConfiguration conf_data;
};
#endif // __NGINX_ATTACHMENT_CONFIG_H__

View File

@@ -0,0 +1,163 @@
// 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 "nginx_attachment_metric.h"
USE_DEBUG_FLAG(D_METRICS_NGINX_ATTACHMENT);
void
nginxAttachmentEvent::resetAllCounters()
{
successfull_registrations_counter = 0;
failed_registrations_counter = 0;
failed_connections_counter = 0;
accept_verdict_counter = 0;
inspect_verdict_counter = 0;
drop_verdict_counter = 0;
inject_verdict_counter = 0;
irrelevant_verdict_counter = 0;
reconf_verdict_counter = 0;
wait_verdict_counter = 0;
}
void
nginxAttachmentEvent::addNetworkingCounter(networkVerdict _verdict)
{
switch (_verdict) {
case networkVerdict::REGISTRATION_SUCCESS: {
successfull_registrations_counter += 1;
break;
}
case networkVerdict::REGISTRATION_FAIL: {
failed_registrations_counter += 1;
break;
}
case networkVerdict::CONNECTION_FAIL: {
failed_connections_counter += 1;
break;
}
default:
dbgWarning(D_METRICS_NGINX_ATTACHMENT) << "Unsupported metric type. Type: " << static_cast<int>(_verdict);
return;
}
}
void
nginxAttachmentEvent::addTrafficVerdictCounter(trafficVerdict _verdict)
{
switch (_verdict) {
case trafficVerdict::INSPECT: {
inspect_verdict_counter += 1;
break;
}
case trafficVerdict::ACCEPT: {
accept_verdict_counter += 1;
break;
}
case trafficVerdict::DROP: {
drop_verdict_counter += 1;
break;
}
case trafficVerdict::INJECT: {
inject_verdict_counter += 1;
break;
}
case trafficVerdict::IRRELEVANT: {
irrelevant_verdict_counter += 1;
break;
}
case trafficVerdict::RECONF: {
reconf_verdict_counter += 1;
break;
}
case trafficVerdict::WAIT: {
wait_verdict_counter += 1;
break;
}
default:
dbgWarning(D_METRICS_NGINX_ATTACHMENT) << "Unsupported metric type. Type: " << static_cast<int>(_verdict);
return;
}
}
void
nginxAttachmentEvent::addResponseInspectionCounter(uint64_t _counter)
{
response_inspection_counter += _counter;
}
uint64_t
nginxAttachmentEvent::getNetworkingCounter(networkVerdict _verdict) const
{
switch (_verdict) {
case networkVerdict::REGISTRATION_SUCCESS:
return successfull_registrations_counter;
case networkVerdict::REGISTRATION_FAIL:
return failed_registrations_counter;
case networkVerdict::CONNECTION_FAIL:
return failed_connections_counter;
default:
dbgWarning(D_METRICS_NGINX_ATTACHMENT) << "Unsupported metric type. Type: " << static_cast<int>(_verdict);
return 0;
}
}
uint64_t
nginxAttachmentEvent::getTrafficVerdictCounter(trafficVerdict _verdict) const
{
switch (_verdict) {
case trafficVerdict::INSPECT:
return inspect_verdict_counter;
case trafficVerdict::ACCEPT:
return accept_verdict_counter;
case trafficVerdict::DROP:
return drop_verdict_counter;
case trafficVerdict::INJECT:
return inject_verdict_counter;
case trafficVerdict::IRRELEVANT:
return irrelevant_verdict_counter;
case trafficVerdict::RECONF:
return reconf_verdict_counter;
case trafficVerdict::WAIT:
return wait_verdict_counter;
default:
dbgWarning(D_METRICS_NGINX_ATTACHMENT) << "Unsupported metric type. Type: " << static_cast<int>(_verdict);
return 0;
}
}
uint64_t
nginxAttachmentEvent::getResponseInspectionCounter() const
{
return response_inspection_counter;
}
void
nginxAttachmentMetric::upon(const nginxAttachmentEvent &event)
{
successfull_registrations.report(
event.getNetworkingCounter(nginxAttachmentEvent::networkVerdict::REGISTRATION_SUCCESS)
);
failed_registrations.report(
event.getNetworkingCounter(nginxAttachmentEvent::networkVerdict::REGISTRATION_FAIL)
);
failed_connections.report(event.getNetworkingCounter(nginxAttachmentEvent::networkVerdict::CONNECTION_FAIL));
inspect_verdict.report(event.getTrafficVerdictCounter(nginxAttachmentEvent::trafficVerdict::INSPECT));
accept_verdict.report(event.getTrafficVerdictCounter(nginxAttachmentEvent::trafficVerdict::ACCEPT));
drop_verdict.report(event.getTrafficVerdictCounter(nginxAttachmentEvent::trafficVerdict::DROP));
inject_verdict.report(event.getTrafficVerdictCounter(nginxAttachmentEvent::trafficVerdict::INJECT));
irrelevant_verdict.report(event.getTrafficVerdictCounter(nginxAttachmentEvent::trafficVerdict::IRRELEVANT));
reconf_verdict.report(event.getTrafficVerdictCounter(nginxAttachmentEvent::trafficVerdict::RECONF));
response_inspection.report(event.getResponseInspectionCounter());
}

View File

@@ -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.
#include "nginx_attachment_opaque.h"
#include "boost/uuid/uuid.hpp"
#include "boost/uuid/uuid_generators.hpp"
#include "boost/uuid/uuid_io.hpp"
#include "config.h"
#include "sasal.h"
#include "virtual_modifiers.h"
SASAL_START // HTTP Manager - Transaction data
using namespace std;
using namespace boost::uuids;
USE_DEBUG_FLAG(D_HTTP_MANAGER);
NginxAttachmentOpaque::NginxAttachmentOpaque(HttpTransactionData _transaction_data)
:
TableOpaqueSerialize<NginxAttachmentOpaque>(this),
transaction_data(move(_transaction_data)),
ctx(),
session_tenant(),
uuid()
{
try {
uuid = to_string(boost::uuids::random_generator()());
} catch (const boost::uuids::entropy_error &e) {
dbgWarning(D_HTTP_MANAGER) << "Failed to generate UUID. Error: " << e.what();
}
dbgTrace(D_HTTP_MANAGER) << "Creating nginx opaque environment from: " << transaction_data;
response_compression_stream = initCompressionStream();
auto client_ip = transaction_data.getSourceIP();
std::stringstream client_ip_str;
client_ip_str << client_ip;
setSourceIdentifier("sourceip", client_ip_str.str());
ctx.registerValue("eventReferenceId", uuid, EnvKeyAttr::LogSection::DATA);
ctx.registerValue<string>(HttpTransactionData::http_proto_ctx, transaction_data.getHttpProtocol());
ctx.registerValue<string>(HttpTransactionData::method_ctx, transaction_data.getHttpMethod());
ctx.registerValue<string>(HttpTransactionData::host_name_ctx, transaction_data.getDestinationHost());
ctx.registerValue<uint16_t>(HttpTransactionData::listening_port_ctx, transaction_data.getListeningPort());
ctx.registerValue<IPAddr>(HttpTransactionData::listening_ip_ctx, transaction_data.getListeningIP());
ctx.registerValue<IPAddr>(HttpTransactionData::client_ip_ctx, transaction_data.getSourceIP());
ctx.registerValue<uint16_t>(HttpTransactionData::client_port_ctx, transaction_data.getSourcePort());
ctx.registerFunc<string>(HttpTransactionData::source_identifier, [this](){ return source_identifier; });
ctx.registerValue<string>(HttpTransactionData::uri_ctx, transaction_data.getURI());
auto decoder = makeVirtualContainer<HexDecoder<'%'>>(transaction_data.getURI());
string decoded_url(decoder.begin(), decoder.end());
auto question_mark_location = decoded_url.find('?');
if (question_mark_location != string::npos) {
ctx.registerValue(HttpTransactionData::uri_query_decoded, decoded_url.substr(question_mark_location + 1));
}
ctx.registerValue(HttpTransactionData::uri_path_decoded, decoded_url.substr(0, question_mark_location));
}
NginxAttachmentOpaque::~NginxAttachmentOpaque()
{
finiCompressionStream(response_compression_stream);
}
// LCOV_EXCL_START - sync functions, can only be tested once the sync module exists
std::unique_ptr<TableOpaqueBase>
NginxAttachmentOpaque::prototype()
{
return make_unique<NginxAttachmentOpaque>(HttpTransactionData());
}
// LCOV_EXCL_STOP
void
NginxAttachmentOpaque::setSessionTenant(const string &tenant)
{
session_tenant = tenant;
Singleton::Consume<I_Environment>::by<NginxAttachmentOpaque>()->setActiveTenant(session_tenant);
}
void
NginxAttachmentOpaque::setSourceIdentifier(const string &header_key, const string &new_source_identifier)
{
identifier_type = header_key;
source_identifier = new_source_identifier;
}
const string &
NginxAttachmentOpaque::getSourceIdentifiersType() const
{
return identifier_type;
}
void
NginxAttachmentOpaque::addToSavedData(const string &name, const string &data)
{
saved_data[name] += data;
ctx.registerValue(name, saved_data[name]);
}
void
NginxAttachmentOpaque::setSavedData(const string &name, const string &data, EnvKeyAttr::LogSection log_ctx)
{
saved_data[name] = data;
ctx.registerValue(name, data, log_ctx);
}
SASAL_END

View File

@@ -0,0 +1,94 @@
// 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 __NGINX_ATTACHMENT_OPAQUE_H__
#define __NGINX_ATTACHMENT_OPAQUE_H__
#include <string>
#include <set>
#include <map>
#include "compression_utils.h"
#include "generic_rulebase/generic_rulebase_context.h"
#include "http_transaction_data.h"
#include "table_opaque.h"
#include "context.h"
#include "i_environment.h"
#include "buffer.h"
class NginxAttachmentOpaque : public TableOpaqueSerialize<NginxAttachmentOpaque>, Singleton::Consume<I_Environment>
{
public:
NginxAttachmentOpaque(HttpTransactionData transaction_data);
~NginxAttachmentOpaque();
void
activateContext()
{
ctx.activate();
gen_ctx.activate();
if (session_tenant != "") {
Singleton::Consume<I_Environment>::by<NginxAttachmentOpaque>()->setActiveTenant(session_tenant);
}
}
void
deactivateContext()
{
if (session_tenant != "") {
Singleton::Consume<I_Environment>::by<NginxAttachmentOpaque>()->unsetActiveTenant();
}
gen_ctx.deactivate();
ctx.deactivate();
}
CompressionStream * getResponseCompressionStream() { return response_compression_stream; }
HttpTransactionData & getTransactionData() { return transaction_data; }
// LCOV_EXCL_START - sync functions, can only be tested once the sync module exists
template <typename T> void serialize(T &, uint) {}
static std::unique_ptr<TableOpaqueBase> prototype();
// LCOV_EXCL_STOP
static const std::string name() { return "NginxAttachmentOpaque"; }
static uint currVer() { return 0; }
static uint minVer() { return 0; }
const std::string & getSessionTenant() const { return session_tenant; }
void setSessionTenant(const std::string &tenant);
void setSourceIdentifier(const std::string &header_key, const std::string &source_identifier);
const std::string & getSourceIdentifiersType() const;
const std::string & getSessionUUID() const { return uuid; }
void addToSavedData(const std::string &name, const std::string &data);
void setSavedData(
const std::string &name,
const std::string &data,
EnvKeyAttr::LogSection log_ctx = EnvKeyAttr::LogSection::NONE
);
private:
CompressionStream *response_compression_stream;
HttpTransactionData transaction_data;
GenericRulebaseContext gen_ctx;
Context ctx;
std::string session_tenant;
std::string uuid;
std::string source_identifier;
std::string identifier_type;
std::map<std::string, std::string> saved_data;
};
#endif // __NGINX_ATTACHMENT_OPAQUE_H__

View File

@@ -0,0 +1,502 @@
// 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 "nginx_intaker_metric.h"
USE_DEBUG_FLAG(D_METRICS_NGINX_ATTACHMENT);
void
nginxIntakerEvent::resetAllCounters()
{
successfull_inspection_counter = 0;
open_failure_inspection_counter = 0;
close_failure_inspection_counter = 0;
transparent_mode_counter = 0;
total_transparent_time = 0;
accept_verdict_counter = 0;
inspect_verdict_counter = 0;
drop_verdict_counter = 0;
inject_verdict_counter = 0;
irrelevant_verdict_counter = 0;
reconf_verdict_counter = 0;
wait_verdict_counter = 0;
req_failed_compression_counter = 0;
res_failed_compression_counter = 0;
req_failed_decompression_counter = 0;
res_failed_decompression_counter = 0;
req_successful_compression_counter = 0;
res_successful_compression_counter = 0;
req_successful_decompression_counter = 0;
res_successful_decompression_counter = 0;
corrupted_zip_skipped_session_counter = 0;
thread_timeout = 0;
reg_thread_timeout = 0;
req_header_thread_timeout = 0;
req_body_thread_timeout = 0;
res_header_thread_timeout = 0;
res_body_thread_timeout = 0;
thread_failure = 0;
req_proccessing_timeout = 0;
res_proccessing_timeout = 0;
req_failed_to_reach_upstream = 0;
cpu_event.setCPU(0);
}
ngx_http_plugin_metric_type_e
nginxIntakerEvent::EnumOfIndex(int i)
{
return static_cast<ngx_http_plugin_metric_type_e>(i);
}
void
nginxIntakerEvent::addPluginMetricCounter(const ngx_http_cp_metric_data_t *recieved_metric_data)
{
for (int i = 0; i < static_cast<int>(ngx_http_plugin_metric_type_e::METRIC_TYPES_COUNT); i++) {
ngx_http_plugin_metric_type_e metric_type = EnumOfIndex(i);
uint64_t amount = recieved_metric_data->data[i];
switch (metric_type) {
case ngx_http_plugin_metric_type_e::INSPECTION_SUCCESSES_COUNT: {
successfull_inspection_counter += amount;
break;
}
case ngx_http_plugin_metric_type_e::INSPECTION_OPEN_FAILURES_COUNT: {
open_failure_inspection_counter += amount;
break;
}
case ngx_http_plugin_metric_type_e::INSPECTION_CLOSE_FAILURES_COUNT: {
close_failure_inspection_counter += amount;
break;
}
case ngx_http_plugin_metric_type_e::TRANSPARENTS_COUNT: {
transparent_mode_counter += amount;
break;
}
case ngx_http_plugin_metric_type_e::TOTAL_TRANSPARENTS_TIME: {
total_transparent_time += amount;
break;
}
case ngx_http_plugin_metric_type_e::INSPECT_VERDICTS_COUNT: {
inspect_verdict_counter += amount;
break;
}
case ngx_http_plugin_metric_type_e::ACCEPT_VERDICTS_COUNT: {
accept_verdict_counter += amount;
break;
}
case ngx_http_plugin_metric_type_e::DROP_VERDICTS_COUNT: {
drop_verdict_counter += amount;
break;
}
case ngx_http_plugin_metric_type_e::INJECT_VERDICTS_COUNT: {
inject_verdict_counter += amount;
break;
}
case ngx_http_plugin_metric_type_e::IRRELEVANT_VERDICTS_COUNT: {
irrelevant_verdict_counter += amount;
break;
}
case ngx_http_plugin_metric_type_e::RECONF_VERDICTS_COUNT: {
reconf_verdict_counter += amount;
break;
}
case ngx_http_plugin_metric_type_e::AVERAGE_OVERALL_PPROCESSING_TIME_UNTIL_VERDICT: {
if (amount > 0) average_overall_processing_time_until_verdict = amount;
break;
}
case ngx_http_plugin_metric_type_e::MAX_OVERALL_PPROCESSING_TIME_UNTIL_VERDICT: {
if (amount > 0) max_overall_processing_time_until_verdict = amount;
break;
}
case ngx_http_plugin_metric_type_e::MIN_OVERALL_PPROCESSING_TIME_UNTIL_VERDICT: {
if (amount > 0) min_overall_processing_time_until_verdict = amount;
break;
}
case ngx_http_plugin_metric_type_e::AVERAGE_REQ_PPROCESSING_TIME_UNTIL_VERDICT: {
if (amount > 0) average_req_processing_time_until_verdict = amount;
break;
}
case ngx_http_plugin_metric_type_e::MAX_REQ_PPROCESSING_TIME_UNTIL_VERDICT: {
if (amount > 0) max_req_processing_time_until_verdict = amount;
break;
}
case ngx_http_plugin_metric_type_e::MIN_REQ_PPROCESSING_TIME_UNTIL_VERDICT: {
if (amount > 0) min_req_processing_time_until_verdict = amount;
break;
}
case ngx_http_plugin_metric_type_e::AVERAGE_RES_PPROCESSING_TIME_UNTIL_VERDICT: {
if (amount > 0) average_res_processing_time_until_verdict = amount;
break;
}
case ngx_http_plugin_metric_type_e::MAX_RES_PPROCESSING_TIME_UNTIL_VERDICT: {
if (amount > 0) max_res_processing_time_until_verdict = amount;
break;
}
case ngx_http_plugin_metric_type_e::MIN_RES_PPROCESSING_TIME_UNTIL_VERDICT: {
if (amount > 0) min_res_processing_time_until_verdict = amount;
break;
}
case ngx_http_plugin_metric_type_e::REQ_FAILED_COMPRESSION_COUNT: {
req_failed_compression_counter += amount;
break;
}
case ngx_http_plugin_metric_type_e::RES_FAILED_COMPRESSION_COUNT: {
res_failed_compression_counter += amount;
break;
}
case ngx_http_plugin_metric_type_e::REQ_FAILED_DECOMPRESSION_COUNT: {
req_failed_decompression_counter += amount;
break;
}
case ngx_http_plugin_metric_type_e::RES_FAILED_DECOMPRESSION_COUNT: {
res_failed_decompression_counter += amount;
break;
}
case ngx_http_plugin_metric_type_e::REQ_SUCCESSFUL_COMPRESSION_COUNT: {
req_successful_compression_counter += amount;
break;
}
case ngx_http_plugin_metric_type_e::RES_SUCCESSFUL_COMPRESSION_COUNT: {
res_successful_compression_counter += amount;
break;
}
case ngx_http_plugin_metric_type_e::REQ_SUCCESSFUL_DECOMPRESSION_COUNT: {
req_successful_decompression_counter += amount;
break;
}
case ngx_http_plugin_metric_type_e::RES_SUCCESSFUL_DECOMPRESSION_COUNT: {
res_successful_decompression_counter += amount;
break;
}
case ngx_http_plugin_metric_type_e::CORRUPTED_ZIP_SKIPPED_SESSION_COUNT: {
corrupted_zip_skipped_session_counter += amount;
break;
}
case ngx_http_plugin_metric_type_e::THREAD_TIMEOUT: {
thread_timeout += amount;
break;
}
case ngx_http_plugin_metric_type_e::REG_THREAD_TIMEOUT: {
reg_thread_timeout += amount;
break;
}
case ngx_http_plugin_metric_type_e::REQ_HEADER_THREAD_TIMEOUT: {
req_header_thread_timeout += amount;
break;
}
case ngx_http_plugin_metric_type_e::REQ_BODY_THREAD_TIMEOUT: {
req_body_thread_timeout += amount;
break;
}
case ngx_http_plugin_metric_type_e::AVERAGE_REQ_BODY_SIZE_UPON_TIMEOUT: {
if (amount > 0) average_req_body_size_upon_timeout = amount;
break;
}
case ngx_http_plugin_metric_type_e::MAX_REQ_BODY_SIZE_UPON_TIMEOUT: {
if (amount > 0) max_req_body_size_upon_timeout = amount;
break;
}
case ngx_http_plugin_metric_type_e::MIN_REQ_BODY_SIZE_UPON_TIMEOUT: {
if (amount > 0) min_req_body_size_upon_timeout = amount;
break;
}
case ngx_http_plugin_metric_type_e::RES_HEADER_THREAD_TIMEOUT: {
res_header_thread_timeout += amount;
break;
}
case ngx_http_plugin_metric_type_e::RES_BODY_THREAD_TIMEOUT: {
res_body_thread_timeout += amount;
break;
}
case ngx_http_plugin_metric_type_e::AVERAGE_RES_BODY_SIZE_UPON_TIMEOUT: {
if (amount > 0) average_res_body_size_upon_timeout = amount;
break;
}
case ngx_http_plugin_metric_type_e::MAX_RES_BODY_SIZE_UPON_TIMEOUT: {
if (amount > 0) max_res_body_size_upon_timeout = amount;
break;
}
case ngx_http_plugin_metric_type_e::MIN_RES_BODY_SIZE_UPON_TIMEOUT: {
if (amount > 0) min_res_body_size_upon_timeout = amount;
break;
}
case ngx_http_plugin_metric_type_e::THREAD_FAILURE: {
thread_failure += amount;
break;
}
case ngx_http_plugin_metric_type_e::REQ_PROCCESSING_TIMEOUT: {
req_proccessing_timeout += amount;
break;
}
case ngx_http_plugin_metric_type_e::RES_PROCCESSING_TIMEOUT: {
res_proccessing_timeout += amount;
break;
}
case ngx_http_plugin_metric_type_e::REQ_FAILED_TO_REACH_UPSTREAM: {
req_failed_to_reach_upstream += amount;
break;
}
case ngx_http_plugin_metric_type_e::CPU_USAGE: {
cpu_event.setCPU(amount);
break;
}
default:
dbgWarning(D_METRICS_NGINX_ATTACHMENT)
<< "Unsupported metric type. Type: " << static_cast<int>(metric_type);
return;
}
}
}
uint64_t
nginxIntakerEvent::getPluginMetricCounter(ngx_http_plugin_metric_type_e metric_type) const
{
switch (metric_type) {
case ngx_http_plugin_metric_type_e::INSPECTION_SUCCESSES_COUNT:
return successfull_inspection_counter;
case ngx_http_plugin_metric_type_e::INSPECTION_OPEN_FAILURES_COUNT:
return open_failure_inspection_counter;
case ngx_http_plugin_metric_type_e::INSPECTION_CLOSE_FAILURES_COUNT:
return close_failure_inspection_counter;
case ngx_http_plugin_metric_type_e::TRANSPARENTS_COUNT:
return transparent_mode_counter;
case ngx_http_plugin_metric_type_e::TOTAL_TRANSPARENTS_TIME:
return total_transparent_time;
case ngx_http_plugin_metric_type_e::INSPECT_VERDICTS_COUNT:
return inspect_verdict_counter;
case ngx_http_plugin_metric_type_e::ACCEPT_VERDICTS_COUNT:
return accept_verdict_counter;
case ngx_http_plugin_metric_type_e::DROP_VERDICTS_COUNT:
return drop_verdict_counter;
case ngx_http_plugin_metric_type_e::INJECT_VERDICTS_COUNT:
return inject_verdict_counter;
case ngx_http_plugin_metric_type_e::IRRELEVANT_VERDICTS_COUNT:
return irrelevant_verdict_counter;
case ngx_http_plugin_metric_type_e::RECONF_VERDICTS_COUNT:
return reconf_verdict_counter;
case ngx_http_plugin_metric_type_e::AVERAGE_OVERALL_PPROCESSING_TIME_UNTIL_VERDICT:
return average_overall_processing_time_until_verdict;
case ngx_http_plugin_metric_type_e::MAX_OVERALL_PPROCESSING_TIME_UNTIL_VERDICT:
return max_overall_processing_time_until_verdict;
case ngx_http_plugin_metric_type_e::MIN_OVERALL_PPROCESSING_TIME_UNTIL_VERDICT:
return min_overall_processing_time_until_verdict;
case ngx_http_plugin_metric_type_e::AVERAGE_REQ_PPROCESSING_TIME_UNTIL_VERDICT:
return average_req_processing_time_until_verdict;
case ngx_http_plugin_metric_type_e::MAX_REQ_PPROCESSING_TIME_UNTIL_VERDICT:
return max_req_processing_time_until_verdict;
case ngx_http_plugin_metric_type_e::MIN_REQ_PPROCESSING_TIME_UNTIL_VERDICT:
return min_req_processing_time_until_verdict;
case ngx_http_plugin_metric_type_e::AVERAGE_RES_PPROCESSING_TIME_UNTIL_VERDICT:
return average_res_processing_time_until_verdict;
case ngx_http_plugin_metric_type_e::MAX_RES_PPROCESSING_TIME_UNTIL_VERDICT:
return max_res_processing_time_until_verdict;
case ngx_http_plugin_metric_type_e::MIN_RES_PPROCESSING_TIME_UNTIL_VERDICT:
return min_res_processing_time_until_verdict;
case ngx_http_plugin_metric_type_e::REQ_FAILED_COMPRESSION_COUNT:
return req_failed_compression_counter;
case ngx_http_plugin_metric_type_e::RES_FAILED_COMPRESSION_COUNT:
return res_failed_compression_counter;
case ngx_http_plugin_metric_type_e::REQ_FAILED_DECOMPRESSION_COUNT:
return req_failed_decompression_counter;
case ngx_http_plugin_metric_type_e::RES_FAILED_DECOMPRESSION_COUNT:
return res_failed_decompression_counter;
case ngx_http_plugin_metric_type_e::REQ_SUCCESSFUL_COMPRESSION_COUNT:
return req_successful_compression_counter;
case ngx_http_plugin_metric_type_e::RES_SUCCESSFUL_COMPRESSION_COUNT:
return res_successful_compression_counter;
case ngx_http_plugin_metric_type_e::REQ_SUCCESSFUL_DECOMPRESSION_COUNT:
return req_successful_decompression_counter;
case ngx_http_plugin_metric_type_e::RES_SUCCESSFUL_DECOMPRESSION_COUNT:
return res_successful_decompression_counter;
case ngx_http_plugin_metric_type_e::CORRUPTED_ZIP_SKIPPED_SESSION_COUNT:
return corrupted_zip_skipped_session_counter;
case ngx_http_plugin_metric_type_e::THREAD_TIMEOUT:
return thread_timeout;
case ngx_http_plugin_metric_type_e::REG_THREAD_TIMEOUT:
return reg_thread_timeout;
case ngx_http_plugin_metric_type_e::REQ_HEADER_THREAD_TIMEOUT:
return req_header_thread_timeout;
case ngx_http_plugin_metric_type_e::REQ_BODY_THREAD_TIMEOUT:
return req_body_thread_timeout;
case ngx_http_plugin_metric_type_e::AVERAGE_REQ_BODY_SIZE_UPON_TIMEOUT:
return average_req_body_size_upon_timeout;
case ngx_http_plugin_metric_type_e::MAX_REQ_BODY_SIZE_UPON_TIMEOUT:
return max_req_body_size_upon_timeout;
case ngx_http_plugin_metric_type_e::MIN_REQ_BODY_SIZE_UPON_TIMEOUT:
return min_req_body_size_upon_timeout;
case ngx_http_plugin_metric_type_e::RES_HEADER_THREAD_TIMEOUT:
return res_header_thread_timeout;
case ngx_http_plugin_metric_type_e::RES_BODY_THREAD_TIMEOUT:
return res_body_thread_timeout;
case ngx_http_plugin_metric_type_e::AVERAGE_RES_BODY_SIZE_UPON_TIMEOUT:
return average_res_body_size_upon_timeout;
case ngx_http_plugin_metric_type_e::MAX_RES_BODY_SIZE_UPON_TIMEOUT:
return max_res_body_size_upon_timeout;
case ngx_http_plugin_metric_type_e::MIN_RES_BODY_SIZE_UPON_TIMEOUT:
return min_res_body_size_upon_timeout;
case ngx_http_plugin_metric_type_e::THREAD_FAILURE:
return thread_failure;
case ngx_http_plugin_metric_type_e::REQ_PROCCESSING_TIMEOUT:
return req_proccessing_timeout;
case ngx_http_plugin_metric_type_e::RES_PROCCESSING_TIMEOUT:
return res_proccessing_timeout;
case ngx_http_plugin_metric_type_e::REQ_FAILED_TO_REACH_UPSTREAM:
return req_failed_to_reach_upstream;
case ngx_http_plugin_metric_type_e::CPU_USAGE:
return static_cast<uint64_t>(cpu_event.getCPU());
default:
dbgWarning(D_METRICS_NGINX_ATTACHMENT)
<< "Unsupported metric type. Type: " << static_cast<int>(metric_type);
return 0;
}
}
void
nginxIntakerMetric::upon(const nginxIntakerEvent &event)
{
successfull_inspection_counter.report(
event.getPluginMetricCounter(ngx_http_plugin_metric_type_e::INSPECTION_SUCCESSES_COUNT)
);
transparent_mode_counter.report(
event.getPluginMetricCounter(ngx_http_plugin_metric_type_e::TRANSPARENTS_COUNT)
);
total_transparent_time.report(
event.getPluginMetricCounter(ngx_http_plugin_metric_type_e::TOTAL_TRANSPARENTS_TIME)
);
open_failure_inspection_counter.report(
event.getPluginMetricCounter(ngx_http_plugin_metric_type_e::INSPECTION_OPEN_FAILURES_COUNT)
);
close_failure_inspection_counter.report(
event.getPluginMetricCounter(ngx_http_plugin_metric_type_e::INSPECTION_CLOSE_FAILURES_COUNT)
);
inject_verdict_counter.report(
event.getPluginMetricCounter(ngx_http_plugin_metric_type_e::INJECT_VERDICTS_COUNT)
);
inspect_verdict_counter.report(
event.getPluginMetricCounter(ngx_http_plugin_metric_type_e::INSPECT_VERDICTS_COUNT)
);
accept_verdict_counter.report(
event.getPluginMetricCounter(ngx_http_plugin_metric_type_e::ACCEPT_VERDICTS_COUNT)
);
drop_verdict_counter.report(
event.getPluginMetricCounter(ngx_http_plugin_metric_type_e::DROP_VERDICTS_COUNT)
);
irrelevant_verdict_counter.report(
event.getPluginMetricCounter(ngx_http_plugin_metric_type_e::IRRELEVANT_VERDICTS_COUNT)
);
reconf_verdict_counter.report(
event.getPluginMetricCounter(ngx_http_plugin_metric_type_e::RECONF_VERDICTS_COUNT)
);
average_overall_processing_time_until_verdict.report(
event.getPluginMetricCounter(ngx_http_plugin_metric_type_e::AVERAGE_OVERALL_PPROCESSING_TIME_UNTIL_VERDICT)
);
max_overall_processing_time_until_verdict.report(
event.getPluginMetricCounter(ngx_http_plugin_metric_type_e::MAX_OVERALL_PPROCESSING_TIME_UNTIL_VERDICT)
);
min_overall_processing_time_until_verdict.report(
event.getPluginMetricCounter(ngx_http_plugin_metric_type_e::MIN_OVERALL_PPROCESSING_TIME_UNTIL_VERDICT)
);
average_req_processing_time_until_verdict.report(
event.getPluginMetricCounter(ngx_http_plugin_metric_type_e::AVERAGE_REQ_PPROCESSING_TIME_UNTIL_VERDICT)
);
max_req_processing_time_until_verdict.report(
event.getPluginMetricCounter(ngx_http_plugin_metric_type_e::MAX_REQ_PPROCESSING_TIME_UNTIL_VERDICT)
);
min_req_processing_time_until_verdict.report(
event.getPluginMetricCounter(ngx_http_plugin_metric_type_e::MIN_REQ_PPROCESSING_TIME_UNTIL_VERDICT)
);
average_res_processing_time_until_verdict.report(
event.getPluginMetricCounter(ngx_http_plugin_metric_type_e::AVERAGE_RES_PPROCESSING_TIME_UNTIL_VERDICT)
);
max_res_processing_time_until_verdict.report(
event.getPluginMetricCounter(ngx_http_plugin_metric_type_e::MAX_RES_PPROCESSING_TIME_UNTIL_VERDICT)
);
min_res_processing_time_until_verdict.report(
event.getPluginMetricCounter(ngx_http_plugin_metric_type_e::MIN_RES_PPROCESSING_TIME_UNTIL_VERDICT)
);
req_failed_compression_counter.report(
event.getPluginMetricCounter(ngx_http_plugin_metric_type_e::REQ_FAILED_COMPRESSION_COUNT)
);
res_failed_compression_counter.report(
event.getPluginMetricCounter(ngx_http_plugin_metric_type_e::RES_FAILED_COMPRESSION_COUNT)
);
req_failed_decompression_counter.report(
event.getPluginMetricCounter(ngx_http_plugin_metric_type_e::REQ_FAILED_DECOMPRESSION_COUNT)
);
res_failed_decompression_counter.report(
event.getPluginMetricCounter(ngx_http_plugin_metric_type_e::RES_FAILED_DECOMPRESSION_COUNT)
);
req_successful_compression_counter.report(
event.getPluginMetricCounter(ngx_http_plugin_metric_type_e::REQ_SUCCESSFUL_COMPRESSION_COUNT)
);
res_successful_compression_counter.report(
event.getPluginMetricCounter(ngx_http_plugin_metric_type_e::RES_SUCCESSFUL_COMPRESSION_COUNT)
);
req_successful_decompression_counter.report(
event.getPluginMetricCounter(ngx_http_plugin_metric_type_e::REQ_SUCCESSFUL_DECOMPRESSION_COUNT)
);
res_successful_decompression_counter.report(
event.getPluginMetricCounter(ngx_http_plugin_metric_type_e::RES_SUCCESSFUL_DECOMPRESSION_COUNT)
);
corrupted_zip_skipped_session_counter.report(
event.getPluginMetricCounter(ngx_http_plugin_metric_type_e::CORRUPTED_ZIP_SKIPPED_SESSION_COUNT)
);
thread_timeout.report(
event.getPluginMetricCounter(ngx_http_plugin_metric_type_e::THREAD_TIMEOUT)
);
reg_thread_timeout.report(
event.getPluginMetricCounter(ngx_http_plugin_metric_type_e::REG_THREAD_TIMEOUT)
);
req_header_thread_timeout.report(
event.getPluginMetricCounter(ngx_http_plugin_metric_type_e::REQ_HEADER_THREAD_TIMEOUT)
);
req_body_thread_timeout.report(
event.getPluginMetricCounter(ngx_http_plugin_metric_type_e::REQ_BODY_THREAD_TIMEOUT)
);
average_req_body_size_upon_timeout.report(
event.getPluginMetricCounter(ngx_http_plugin_metric_type_e::AVERAGE_REQ_BODY_SIZE_UPON_TIMEOUT)
);
max_req_body_size_upon_timeout.report(
event.getPluginMetricCounter(ngx_http_plugin_metric_type_e::MAX_REQ_BODY_SIZE_UPON_TIMEOUT)
);
min_req_body_size_upon_timeout.report(
event.getPluginMetricCounter(ngx_http_plugin_metric_type_e::MIN_REQ_BODY_SIZE_UPON_TIMEOUT)
);
res_header_thread_timeout.report(
event.getPluginMetricCounter(ngx_http_plugin_metric_type_e::RES_HEADER_THREAD_TIMEOUT)
);
res_body_thread_timeout.report(
event.getPluginMetricCounter(ngx_http_plugin_metric_type_e::RES_BODY_THREAD_TIMEOUT)
);
average_res_body_size_upon_timeout.report(
event.getPluginMetricCounter(ngx_http_plugin_metric_type_e::AVERAGE_RES_BODY_SIZE_UPON_TIMEOUT)
);
max_res_body_size_upon_timeout.report(
event.getPluginMetricCounter(ngx_http_plugin_metric_type_e::MAX_RES_BODY_SIZE_UPON_TIMEOUT)
);
min_res_body_size_upon_timeout.report(
event.getPluginMetricCounter(ngx_http_plugin_metric_type_e::MIN_RES_BODY_SIZE_UPON_TIMEOUT)
);
thread_failure.report(
event.getPluginMetricCounter(ngx_http_plugin_metric_type_e::THREAD_FAILURE)
);
req_proccessing_timeout.report(
event.getPluginMetricCounter(ngx_http_plugin_metric_type_e::REQ_PROCCESSING_TIMEOUT)
);
res_proccessing_timeout.report(
event.getPluginMetricCounter(ngx_http_plugin_metric_type_e::RES_PROCCESSING_TIMEOUT)
);
req_failed_to_reach_upstream.report(
event.getPluginMetricCounter(ngx_http_plugin_metric_type_e::REQ_FAILED_TO_REACH_UPSTREAM)
);
event.notifyCPU();
}

View File

@@ -0,0 +1,369 @@
// 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 "nginx_parser.h"
#include "config.h"
#include <algorithm>
#include "connkey.h"
#include "compression_utils.h"
#include "nginx_attachment.h"
#include "nginx_attachment_opaque.h"
#include "user_identifiers_config.h"
#include "debug.h"
using namespace std;
USE_DEBUG_FLAG(D_NGINX_ATTACHMENT_PARSER);
Buffer NginxParser::tenant_header_key = Buffer();
static const Buffer proxy_ip_header_key("X-Forwarded-For", 15, Buffer::MemoryType::STATIC);
static const Buffer source_ip("sourceip", 8, Buffer::MemoryType::STATIC);
map<Buffer, CompressionType> NginxParser::content_encodings = {
{Buffer("identity"), CompressionType::NO_COMPRESSION},
{Buffer("gzip"), CompressionType::GZIP},
{Buffer("deflate"), CompressionType::ZLIB}
};
Maybe<HttpTransactionData>
NginxParser::parseStartTrasaction(const Buffer &data)
{
return HttpTransactionData::createTransactionData(data);
}
Maybe<ResponseCode>
NginxParser::parseResponseCode(const Buffer &data)
{
if (data.size() < sizeof(uint16_t)) {
dbgWarning(D_NGINX_ATTACHMENT_PARSER) << "Failed to get response code";
return genError("Response code size is lower than uint16_t");
}
return reinterpret_cast<const uint16_t *>(data.data())[0];
}
Maybe<uint64_t>
NginxParser::parseContentLength(const Buffer &data)
{
if (data.size() < sizeof(uint64_t)) {
dbgWarning(D_NGINX_ATTACHMENT_PARSER)
<< "Failed to get content length";
return genError("Content length size is lower than uint64");
}
return **data.getTypePtr<uint64_t>(0);
}
Maybe<Buffer>
genHeaderPart(const Buffer &raw_data, uint16_t &cur_pos)
{
if (cur_pos >= raw_data.size()) return genError("Current header data possession is after header part end");
auto value = raw_data.getTypePtr<uint16_t>(cur_pos);
if (!value.ok()) {
return genError("Failed to get header part size: " + value.getErr());
}
uint16_t part_len = *(value.unpack());
cur_pos += sizeof(uint16_t);
if (cur_pos + part_len > raw_data.size()) return genError("Header data extends beyond current buffer");
const u_char *part_data = raw_data.data();
Buffer header_part(part_data + cur_pos, part_len, Buffer::MemoryType::VOLATILE);
cur_pos += part_len;
return header_part;
}
Maybe<vector<HttpHeader>>
genHeaders(const Buffer &raw_data)
{
dbgFlow(D_NGINX_ATTACHMENT_PARSER) << "Generating headers";
uint16_t cur_pos = 0;
auto is_last_header_data = raw_data.getTypePtr<uint8_t>(cur_pos);
if (!is_last_header_data.ok()) {
return genError("Failed to get 'is last header' value: " + is_last_header_data.getErr());
}
bool is_last_header = *(is_last_header_data.unpack()) == 1;
dbgTrace(D_NGINX_ATTACHMENT_PARSER)
<< "Current header bulk "
<< (is_last_header ? "contains " : "does not contain ")
<< "last header";
cur_pos += sizeof(uint8_t);
auto part_count = raw_data.getTypePtr<uint8_t>(cur_pos);
if (!part_count.ok()) {
return genError("Failed to get part count value: " + part_count.getErr());
}
dbgTrace(D_NGINX_ATTACHMENT_PARSER) << "Current header bulk index: " << to_string(*(part_count.unpack()));
static const string key_val_desc[] = {"key", "value"};
Maybe<Buffer> header[2] = {Buffer(), Buffer()};
vector<HttpHeader> headers;
cur_pos += sizeof(uint8_t);
uint8_t cur_part = *(part_count.unpack());
while (cur_pos < raw_data.size()) {
for (int i = 0 ; i < 2 ; i ++) {
dbgTrace(D_NGINX_ATTACHMENT_PARSER)
<< "Generating"
<< (is_last_header ? " last " : " ")
<< "header's "
<< key_val_desc[i];
header[i] = genHeaderPart(raw_data, cur_pos);
if (!header[i].ok()) {
return genError("Failed to generate header's " + key_val_desc[i] + ":" + header[i].getErr());
}
dbgTrace(D_NGINX_ATTACHMENT_PARSER)
<< "Successfully generated header part. Header part type:"
<< key_val_desc[i]
<< ", data: '"
<< dumpHex(header[i].unpack())
<< "', size: "
<< header[i].unpack().size();
}
// is_last_header in bulk relates only to the last header in the bulk.
headers.emplace_back(
header[0].unpack(),
header[1].unpack(),
cur_part,
cur_pos >= raw_data.size() && is_last_header
);
dbgTrace(D_NGINX_ATTACHMENT_PARSER) << "end pos: " << cur_pos;
cur_part++;
}
return headers;
}
Maybe<vector<HttpHeader>>
NginxParser::parseRequestHeaders(const Buffer &data)
{
auto parsed_headers = genHeaders(data);
if (!parsed_headers.ok()) return parsed_headers.passErr();
auto i_transaction_table = Singleton::Consume<I_TableSpecific<SessionID>>::by<NginxAttachment>();
for (const HttpHeader &header : *parsed_headers) {
auto source_identifiers = getConfigurationWithDefault<UsersAllIdentifiersConfig>(
UsersAllIdentifiersConfig(),
"rulebase",
"usersIdentifiers"
);
source_identifiers.parseRequestHeaders(header);
NginxAttachmentOpaque &opaque = i_transaction_table->getState<NginxAttachmentOpaque>();
opaque.addToSavedData(
HttpTransactionData::req_headers,
static_cast<string>(header.getKey()) + ": " + static_cast<string>(header.getValue()) + "\r\n"
);
if (NginxParser::tenant_header_key == header.getKey()) {
dbgDebug(D_NGINX_ATTACHMENT_PARSER)
<< "Identified active tenant header. Key: "
<< dumpHex(header.getKey())
<< ", Value: "
<< dumpHex(header.getValue());
string active_tenant(static_cast<string>(header.getValue()));
opaque.setSessionTenant(active_tenant);
} else if (proxy_ip_header_key == header.getKey()) {
source_identifiers.setXFFValuesToOpaqueCtx(header, UsersAllIdentifiersConfig::ExtractType::PROXYIP);
}
}
return parsed_headers;
}
Maybe<vector<HttpHeader>>
NginxParser::parseResponseHeaders(const Buffer &data)
{
return genHeaders(data);
}
Maybe<Buffer>
decompressBuffer(CompressionStream *compression_stream, const Buffer &compressed_buffer)
{
if (compressed_buffer.size() == 0) return Buffer();
auto compression_result = decompressData(compression_stream, compressed_buffer.size(), compressed_buffer.data());
if (!compression_result.ok) return genError("Failed to decompress data");
if (compression_result.output == nullptr) return Buffer();;
Buffer decompressed_buffer(
compression_result.output,
compression_result.num_output_bytes,
Buffer::MemoryType::OWNED
);
free(compression_result.output);
return decompressed_buffer;
}
Maybe<Buffer>
parseCompressedHttpBodyData(CompressionStream *compression_stream, const Buffer &body_raw_data)
{
if (compression_stream == nullptr) return genError("Cannot decompress body without compression stream");
Maybe<Buffer> decompressed_buffer_maybe = decompressBuffer(compression_stream, body_raw_data);
if (!decompressed_buffer_maybe.ok()) {
return genError("Failed to decompress buffer. Error: " + decompressed_buffer_maybe.getErr());
}
return decompressed_buffer_maybe.unpack();
}
Maybe<HttpBody>
genBody(const Buffer &raw_response_body, CompressionStream *compression_stream = nullptr)
{
uint offset = 0;
auto is_last_part_maybe = raw_response_body.getTypePtr<uint8_t>(offset);
if (!is_last_part_maybe.ok()) {
return genError("Failed to get 'is last part' value: " + is_last_part_maybe.getErr());
}
bool is_last_part = *is_last_part_maybe.unpack();
offset += sizeof(uint8_t);
auto part_count_maybe = raw_response_body.getTypePtr<uint8_t>(offset);
if (!part_count_maybe.ok()) {
return genError("Failed to get part count value: " + part_count_maybe.getErr());
}
uint8_t body_chunk_index = *part_count_maybe.unpack();
offset += sizeof(uint8_t);
Buffer body_raw_data(
raw_response_body.data() + offset,
raw_response_body.size() - offset,
Buffer::MemoryType::VOLATILE
);
if (compression_stream == nullptr) {
dbgTrace(D_NGINX_ATTACHMENT_PARSER) << "Successfully generated body chunk from non compressed buffer";
return HttpBody(body_raw_data, is_last_part, body_chunk_index);
}
Maybe<Buffer> body_data_maybe = parseCompressedHttpBodyData(compression_stream, body_raw_data);
if (!body_data_maybe.ok()) {
dbgWarning(D_NGINX_ATTACHMENT_PARSER)
<< "Failed to decompress body chunk. Chunk index: "
<< to_string(body_chunk_index)
<< ", raw input size: "
<< body_raw_data.size();
return genError("Failed to parse HTTP body data: " + body_data_maybe.getErr());
}
dbgTrace(D_NGINX_ATTACHMENT_PARSER)
<< "Successfully generated decompressed body chunk. Compressed original size: "
<< body_raw_data.size();
return HttpBody(body_data_maybe.unpack(), is_last_part, body_chunk_index);
}
Maybe<HttpBody>
NginxParser::parseRequestBody(const Buffer &data)
{
Maybe<HttpBody> body = genBody(data);
if (!body.ok()) return genError("Failed to generate body from buffer: " + body.getErr());
dbgTrace(D_NGINX_ATTACHMENT_PARSER)
<< "Successfully generated request body chunk. Chunk index: "
<< to_string(body.unpack().getBodyChunkIndex())
<< ", is last chunk: "
<< (body.unpack().isLastChunk() ? "true" : "false")
<< ", size: "
<< body.unpack().getData().size()
<< ", value: "
<< dumpHex(body.unpack().getData());
auto i_transaction_table = Singleton::Consume<I_TableSpecific<SessionID>>::by<NginxAttachment>();
auto &state = i_transaction_table->getState<NginxAttachmentOpaque>();
state.setSavedData(HttpTransactionData::req_body, (*body).getData());
return body;
}
Maybe<HttpBody>
NginxParser::parseResponseBody(const Buffer &raw_response_body, CompressionStream *compression_stream)
{
Maybe<HttpBody> body = genBody(raw_response_body, compression_stream);
if (!body.ok()) return genError("Failed to generate body from buffer: " + body.getErr());
dbgTrace(D_NGINX_ATTACHMENT_PARSER)
<< "Successfully generated response body chunk. Chunk index: "
<< to_string(body.unpack().getBodyChunkIndex())
<< ", is last chunk: "
<< (body.unpack().isLastChunk() ? "true" : "false")
<< ", size: "
<< body.unpack().getData().size()
<< ", value: "
<< dumpHex(body.unpack().getData());;
return body;
}
Maybe<CompressionType>
NginxParser::parseContentEncoding(const vector<HttpHeader> &headers)
{
static const Buffer content_encoding_header_key("Content-Encoding");
auto it = find_if(
headers.begin(),
headers.end(),
[&] (const HttpHeader &http_header) { return http_header.getKey() == content_encoding_header_key; }
);
if (it == headers.end()) {
dbgTrace(D_NGINX_ATTACHMENT_PARSER)
<< "Headers do not contain \"Content-Encoding\" header: "
<< "body is expected to be plain-text";
return CompressionType::NO_COMPRESSION;
}
dbgTrace(D_NGINX_ATTACHMENT_PARSER)
<< "Found header with key \"Content-Encoding\". Value: "
<< dumpHex((*it).getValue());
auto content_encoding_maybe = convertToContentEncoding((*it).getValue());
if (!content_encoding_maybe.ok()) {
return genError(
"Failed to parse value of \"Content-Encoding\" header: " +
content_encoding_maybe.getErr()
);
}
dbgTrace(D_NGINX_ATTACHMENT_PARSER) << "Successfully parsed value of \"Content-Encoding\" header";
return content_encoding_maybe.unpack();
}
Maybe<CompressionType>
NginxParser::convertToContentEncoding(const Buffer &content_encoding_header_value)
{
if (content_encoding_header_value.contains(',')) {
return genError("Multiple content encodings for a specific HTTP request/response body are not supported");
}
if (content_encodings.find(content_encoding_header_value) == content_encodings.end()) {
return genError(
"Unsupported or undefined \"Content-Encoding\" value: " +
static_cast<string>(content_encoding_header_value)
);
}
return content_encodings[content_encoding_header_value];
}

View File

@@ -0,0 +1,45 @@
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef __NGINX_PARSER_H__
#define __NGINX_PARSER_H__
#include <vector>
#include "compression_utils.h"
#include "nginx_attachment_common.h"
#include "http_transaction_common.h"
#include "http_inspection_events.h"
#include "i_encryptor.h"
class NginxParser : Singleton::Consume<I_Encryptor>
{
public:
static Maybe<HttpTransactionData> parseStartTrasaction(const Buffer &data);
static Maybe<ResponseCode> parseResponseCode(const Buffer &data);
static Maybe<uint64_t> parseContentLength(const Buffer &data);
static Maybe<std::vector<HttpHeader>> parseRequestHeaders(const Buffer &data);
static Maybe<std::vector<HttpHeader>> parseResponseHeaders(const Buffer &data);
static Maybe<HttpBody> parseRequestBody(const Buffer &data);
static Maybe<HttpBody> parseResponseBody(const Buffer &raw_response_body, CompressionStream *compression_stream);
static Maybe<CompressionType> parseContentEncoding(const std::vector<HttpHeader> &headers);
static Buffer tenant_header_key;
private:
static Maybe<CompressionType> convertToContentEncoding(const Buffer &content_encoding_header_value);
static std::map<Buffer, CompressionType> content_encodings;
};
#endif // __NGINX_PARSER_H__

View File

@@ -0,0 +1,447 @@
// 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 "user_identifiers_config.h"
#include "buffer.h"
#include "nginx_attachment.h"
#include "nginx_attachment_opaque.h"
#include "nginx_parser.h"
#include "cidrs_data.h"
using namespace std;
USE_DEBUG_FLAG(D_NGINX_ATTACHMENT_PARSER);
static const Buffer header_key("headerkey", 9, Buffer::MemoryType::STATIC);
static const Buffer jwt("authorization", 13, Buffer::MemoryType::STATIC);
static const Buffer xff("x-forwarded-for", 15, Buffer::MemoryType::STATIC);
static const Buffer cookie("cookie", 6, Buffer::MemoryType::STATIC);
static const Buffer source_ip("sourceip", 8, Buffer::MemoryType::STATIC);
static const Buffer oauth("_oauth2_proxy", 13, Buffer::MemoryType::STATIC);
static const Buffer empty_buffer("", 0, Buffer::MemoryType::STATIC);
const static string jwt_prefix = "Bearer ";
UsersAllIdentifiersConfig::UsersIdentifiersConfig::UsersIdentifiersConfig() : source_identifier(source_ip){};
UsersAllIdentifiersConfig::UsersIdentifiersConfig::UsersIdentifiersConfig(const std::string &identifier)
:
source_identifier(identifier)
{}
bool
UsersAllIdentifiersConfig::UsersIdentifiersConfig::operator==(const UsersIdentifiersConfig &other) const
{
return source_identifier == other.source_identifier;
}
void
UsersAllIdentifiersConfig::UsersIdentifiersConfig::load(cereal::JSONInputArchive &ar)
{
parseJSONKey<string>("sourceIdentifier", source_identifier, ar);
parseJSONKey<vector<string>>("identifierValues", identifier_values, ar);
}
bool
UsersAllIdentifiersConfig::UsersIdentifiersConfig::isEqualSourceIdentifier(const string &other) const
{
if (source_identifier.size() != other.size()) return false;
return equal(
source_identifier.begin(),
source_identifier.end(),
other.begin(),
[] (char c1, char c2) { return tolower(c1) == tolower(c2); }
);
}
UsersAllIdentifiersConfig::UsersAllIdentifiersConfig()
{
}
vector<string>
UsersAllIdentifiersConfig::getHeaderValuesFromConfig(const string &header_key) const
{
for (auto user_identifier : user_identifiers) {
if (user_identifier.isEqualSourceIdentifier(header_key)) {
dbgDebug(D_NGINX_ATTACHMENT_PARSER) << "Match source identifier is found";
return user_identifier.getIdentifierValues();
}
}
return vector<string>();
}
void
UsersAllIdentifiersConfig::load(cereal::JSONInputArchive &ar)
{
vector<UsersIdentifiersConfig> tmp_user_identifiers;
parseJSONKey<vector<UsersIdentifiersConfig>>("sourceIdentifiers", tmp_user_identifiers, ar);
user_identifiers.clear();
user_identifiers.reserve(tmp_user_identifiers.size() + 1);
for (auto &identifier : tmp_user_identifiers) {
if (identifier.getSourceIdentifier() == header_key) {
for (const auto &header : identifier.getIdentifierValues()) {
user_identifiers.emplace_back(header);
}
} else {
user_identifiers.push_back(identifier);
}
}
vector<UsersIdentifiersConfig> default_order = {
UsersIdentifiersConfig(cookie),
UsersIdentifiersConfig(jwt),
UsersIdentifiersConfig(xff)
};
auto last_user_defined_header = find_first_of(
default_order.rbegin(),
default_order.rend(),
user_identifiers.begin(),
user_identifiers.end()
);
if (last_user_defined_header == default_order.rend()) {
user_identifiers.insert(user_identifiers.end(), default_order.begin(), default_order.end());
} else {
auto last_defined_forwards = find(default_order.begin(), default_order.end(), *last_user_defined_header);
user_identifiers.insert(user_identifiers.end(), last_defined_forwards + 1, default_order.end());
}
}
static bool
compareBufferWithoutCase(const Buffer &b1, const Buffer &b2)
{
if (b1.size() != b2.size()) return false;
return equal(b1.begin(), b1.end(), b2.begin(), [] (u_char c1, u_char c2) { return tolower(c1) == tolower(c2); });
}
void
UsersAllIdentifiersConfig::setIdentifierTopaqueCtx(const HttpHeader &header) const
{
if (compareBufferWithoutCase(jwt, header.getKey())) {
setJWTValuesToOpaqueCtx(header);
} else if (compareBufferWithoutCase(xff, header.getKey())) {
setXFFValuesToOpaqueCtx(header, ExtractType::SOURCEIDENTIFIER);
} else if (compareBufferWithoutCase(cookie, header.getKey())) {
setCookieValuesToOpaqueCtx(header);
} else {
setCustomHeaderToOpaqueCtx(header);
}
}
bool
UsersAllIdentifiersConfig::isHigherPriority(const string &current_identifier, const string &header_key) const
{
for (auto user_identifier : user_identifiers) {
if (user_identifier.isEqualSourceIdentifier(current_identifier)) return false;
if (user_identifier.isEqualSourceIdentifier(header_key)) return true;
}
return false;
}
void
UsersAllIdentifiersConfig::setJWTValuesToOpaqueCtx(const HttpHeader &header) const
{
const vector<string> jwt_values = getHeaderValuesFromConfig(header.getKey());
if (jwt_values.size() == 0) {
dbgTrace(D_NGINX_ATTACHMENT_PARSER) << "No JWT keys exists in configuration";
return;
}
if (bcmp(header.getValue().data(), jwt_prefix.c_str(), jwt_prefix.size()) != 0) {
dbgTrace(D_NGINX_ATTACHMENT_PARSER) << "Invalid JWT header, 'Bearer' prefix missing";
return;
}
int start_dot = -1;
int end_dot = -1;
for (uint i = 0 ; i < header.getValue().size() ; i++) {
if (header.getValue()[i] == '.') {
if (start_dot < 0) {
start_dot = i;
} else if (end_dot < 0) {
end_dot = i;
}
}
}
if (start_dot < 0 || end_dot < 0) {
dbgTrace(D_NGINX_ATTACHMENT_PARSER) << "The header does not contain dots";
return;
}
string jwt_str(
reinterpret_cast<const char *>(header.getValue().data()),
start_dot + 1,
end_dot - start_dot - 1
);
I_Encryptor *encryptor = Singleton::Consume<I_Encryptor>::by<NginxParser>();
auto decoded_jwt = encryptor->base64Decode(jwt_str);
dbgDebug(D_NGINX_ATTACHMENT_PARSER) << "Base64 decoded JWT: " << decoded_jwt;
auto i_transaction_table = Singleton::Consume<I_TableSpecific<SessionID>>::by<NginxAttachment>();
if (!i_transaction_table || !i_transaction_table->hasState<NginxAttachmentOpaque>()) {
dbgDebug(D_NGINX_ATTACHMENT_PARSER) << "Can't get the transaction table";
return;
}
NginxAttachmentOpaque &opaque = i_transaction_table->getState<NginxAttachmentOpaque>();
stringstream ss;
ss.str(decoded_jwt);
cereal::JSONInputArchive in_ar(ss);
for (const string &field_name : jwt_values) {
try {
string tmp_val;
in_ar(cereal::make_nvp(field_name, tmp_val));
opaque.setSourceIdentifier(header.getKey(), tmp_val);
dbgDebug(D_NGINX_ATTACHMENT_PARSER)
<< "Added source identifir to context. Key: "
<< field_name
<< ". Value: "
<< tmp_val;
return;
} catch (const cereal::Exception &e) {
dbgTrace(D_NGINX_ATTACHMENT_PARSER)
<< "Unable to find value for the key: "
<< field_name
<< ". Error: "
<< e.what();
}
}
}
static string
stripOptionalPort(const string::const_iterator &first, const string::const_iterator &last)
{
// Microsoft XFF+IPv6+Port yikes - see also here https://github.com/eclipse/jetty.project/issues/3630
if (*first == '[') {
// Possible bracketed IPv6 address such as "[2001:db8::1]" + optional numeric ":<port>"
auto close_bracket = find(first + 1, last, ']');
if (close_bracket == last) return string(first, last);
return string(first+1, close_bracket);
}
auto first_colon = find(first, last, ':');
if (first_colon == last) return string(first, last);
// If there is more than one colon it means its probably IPv6 address without brackets
auto second_colon = find(first_colon + 1, last, ':');
if (second_colon != last) return string(first, last);
// If there's only one colon it can't be IPv6 and can only be IPv4 with port
return string(first, first_colon);
}
static vector<string>
split(const string &str)
{
vector<string> elems;
elems.reserve(str.size() / 8 + 1);
auto sub_start = str.cbegin(), sub_end = str.cbegin();
for (auto iter = str.cbegin(); iter != str.cend(); ++iter) {
if (isspace(*iter)) {
if (sub_start == iter) {
++sub_start;
++sub_end;
}
} else if (*iter == ',') {
if (sub_start != sub_end) {
elems.push_back(stripOptionalPort(sub_start, sub_end));
}
sub_end = iter + 1;
sub_start = iter + 1;
} else {
sub_end = iter + 1;
}
}
if (sub_start != sub_end) {
elems.push_back(stripOptionalPort(sub_start, sub_end));
}
return elems;
}
static bool
isIpTrusted(const string &value, const vector<CIDRSData> &cidr_values)
{
if (cidr_values.empty()) return true;
for(const auto &cidr_data : cidr_values) {
if (cidr_data.contains(value)) return true;
}
return false;
}
Maybe<string>
UsersAllIdentifiersConfig::parseXForwardedFor(const string &str) const
{
vector<string> header_values = split(str);
if (header_values.empty()) return genError("No IP found in the xff header list");
vector<string> xff_values = getHeaderValuesFromConfig("x-forwarded-for");
vector<CIDRSData> cidr_values(xff_values.begin(), xff_values.end());
for (const string &value : header_values) {
if (!IPAddr::createIPAddr(value).ok()) {
dbgWarning(D_NGINX_ATTACHMENT_PARSER) << "Invalid IP address found in the xff header IPs list: " << value;
return genError("Invalid IP address");
}
if (!isIpTrusted(value, cidr_values)) return genError("Untrusted Ip found");
}
return header_values[0];
}
void
UsersAllIdentifiersConfig::setXFFValuesToOpaqueCtx(const HttpHeader &header, ExtractType type) const
{
auto value = parseXForwardedFor(header.getValue());
if (!value.ok()) {
dbgTrace(D_NGINX_ATTACHMENT_PARSER) << "Could not extract source identifier from X-Forwarded-For header";
return;
};
auto i_transaction_table = Singleton::Consume<I_TableSpecific<SessionID>>::by<NginxAttachment>();
if (!i_transaction_table || !i_transaction_table->hasState<NginxAttachmentOpaque>()) {
dbgDebug(D_NGINX_ATTACHMENT_PARSER) << "Can't get the transaction table";
return;
}
NginxAttachmentOpaque &opaque = i_transaction_table->getState<NginxAttachmentOpaque>();
if (type == ExtractType::SOURCEIDENTIFIER) {
opaque.setSourceIdentifier(header.getKey(), value.unpack());
dbgDebug(D_NGINX_ATTACHMENT_PARSER)
<< "Added source identifir to XFF "
<< value.unpack();
} else {
opaque.setSavedData(HttpTransactionData::proxy_ip_ctx, value.unpack());
}
}
void
UsersAllIdentifiersConfig::setCustomHeaderToOpaqueCtx(const HttpHeader &header) const
{
auto i_transaction_table = Singleton::Consume<I_TableSpecific<SessionID>>::by<NginxAttachment>();
if (!i_transaction_table || !i_transaction_table->hasState<NginxAttachmentOpaque>()) {
dbgDebug(D_NGINX_ATTACHMENT_PARSER) << "Can't get the transaction table";
return;
}
i_transaction_table->getState<NginxAttachmentOpaque>().setSourceIdentifier(header.getKey(), header.getValue());
dbgDebug(D_NGINX_ATTACHMENT_PARSER)
<< "Added source identifir to custom header: "
<< static_cast<string>(header.getValue());
return;
}
Maybe<string>
UsersAllIdentifiersConfig::parseCookieElement(
const string::const_iterator &start,
const string::const_iterator &end,
const string &key) const
{
auto curr_pos = start;
// Skip whitespace
for (; curr_pos != end && isspace(*curr_pos); ++curr_pos);
// Check key
for (auto key_pos = key.begin(); key_pos != key.end(); ++key_pos) {
if (curr_pos == end || tolower(*curr_pos) != tolower(*key_pos)) return genError("Key value not found");
++curr_pos;
}
// Skip whitespace
for (; curr_pos != end && isspace(*curr_pos); ++curr_pos);
// Check for '='
if (curr_pos == end || *curr_pos != '=') return genError("Equal sign not found");
++curr_pos;
// Skip whitespace
for (; curr_pos != end && isspace(*curr_pos); ++curr_pos);
auto value_start = curr_pos;
// Read value
for (; curr_pos != end && !isspace(*curr_pos); ++curr_pos);
auto value_end = curr_pos;
// Verify value read currectly - should be only whitespaces to the end;
for (; curr_pos != end && isspace(*curr_pos); ++curr_pos);
if (curr_pos != end) return genError("Unexpected characters when reading a value");
return string(value_start, value_end);
}
Buffer
UsersAllIdentifiersConfig::extractKeyValueFromCookie(const string &cookie_value, const string &key) const
{
auto curr_start = cookie_value.begin();
auto end = cookie_value.end();
while (curr_start != end) {
auto curr_end = find(curr_start, end, ';');
auto res = parseCookieElement(curr_start, curr_end, key);
if (res.ok()) {
if (key != oauth) return *res;
I_Encryptor *encryptor = Singleton::Consume<I_Encryptor>::by<NginxParser>();
auto decoded_value = encryptor->base64Decode(*res);
auto decoded_end = find(decoded_value.begin(), decoded_value.end(), '|');
return Buffer(string(decoded_value.begin(), decoded_end));
}
if (curr_end != end) ++curr_end;
curr_start = curr_end;
}
return empty_buffer;
}
void
UsersAllIdentifiersConfig::setCookieValuesToOpaqueCtx(const HttpHeader &header) const
{
vector<string> cookie_keys = getHeaderValuesFromConfig(header.getKey());
cookie_keys.push_back(oauth);
cookie_keys.push_back("jsessionid");
for (const string &key : cookie_keys) {
string value = extractKeyValueFromCookie(header.getValue(), key);
if (!value.empty()) {
dbgDebug(D_NGINX_ATTACHMENT_PARSER) << "Set source identifier from cookie: Oauth 2";
auto i_transaction_table = Singleton::Consume<I_TableSpecific<SessionID>>::by<NginxAttachment>();
if (!i_transaction_table || !i_transaction_table->hasState<NginxAttachmentOpaque>()) {
dbgDebug(D_NGINX_ATTACHMENT_PARSER) << "Can't get the transaction table";
return;
}
NginxAttachmentOpaque &opaque = i_transaction_table->getState<NginxAttachmentOpaque>();
opaque.setSourceIdentifier(header.getKey(), value);
return;
}
}
}
void
UsersAllIdentifiersConfig::parseRequestHeaders(const HttpHeader &header) const
{
auto i_transaction_table = Singleton::Consume<I_TableSpecific<SessionID>>::by<NginxAttachment>();
if (!i_transaction_table || !i_transaction_table->hasState<NginxAttachmentOpaque>()) {
dbgDebug(D_NGINX_ATTACHMENT_PARSER) << "Can't get the transaction table";
return;
}
NginxAttachmentOpaque &opaque = i_transaction_table->getState<NginxAttachmentOpaque>();
const string &current_identifier = opaque.getSourceIdentifiersType();
if (!isHigherPriority(current_identifier, header.getKey())) return;
setIdentifierTopaqueCtx(header);
}

View File

@@ -0,0 +1,4 @@
add_definitions(-DUSERSPACE)
add_subdirectory(evaluators)
add_library(generic_rulebase generic_rulebase.cc rulebase_config.cc triggers_config.cc parameters_config.cc generic_rulebase_context.cc zones_config.cc zone.cc assets_config.cc match_query.cc)

View File

@@ -0,0 +1,137 @@
// 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 "generic_rulebase/assets_config.h"
#include <string>
#include <algorithm>
#include <unordered_map>
#include "generic_rulebase/generic_rulebase_utils.h"
#include "config.h"
#include "debug.h"
#include "ip_utilities.h"
USE_DEBUG_FLAG(D_RULEBASE_CONFIG);
using namespace std;
void
RuleAsset::load(cereal::JSONInputArchive &archive_in)
{
archive_in(cereal::make_nvp("assetId", asset_id));
archive_in(cereal::make_nvp("assetName", asset_name));
archive_in(cereal::make_nvp("assetUrls", asset_urls));
dbgWarning(D_RULEBASE_CONFIG) << "Adding asset with UID: " << asset_id;
}
void
RuleAsset::AssetUrl::load(cereal::JSONInputArchive &archive_in)
{
archive_in(cereal::make_nvp("protocol", protocol));
transform(protocol.begin(), protocol.end(), protocol.begin(), [](unsigned char c) { return tolower(c); });
archive_in(cereal::make_nvp("ip", ip));
archive_in(cereal::make_nvp("port", port));
int value;
if (protocol == "*") {
is_any_proto = true;
} else {
is_any_proto = false;
try {
value = 0;
if(protocol == "udp") value = IPPROTO_UDP;
if(protocol == "tcp") value = IPPROTO_TCP;
if(protocol == "dccp") value = IPPROTO_DCCP;
if(protocol == "sctp") value = IPPROTO_SCTP;
if(protocol == "icmp") value = IPPROTO_ICMP;
if(protocol == "icmpv6") value = IPPROTO_ICMP;
if (value > static_cast<int>(UINT8_MAX) || value < 0) {
dbgWarning(D_RULEBASE_CONFIG)
<< "provided value is not a legal IP protocol number. Value: "
<< protocol;
} else {
parsed_proto = value;
}
} catch (...) {
dbgWarning(D_RULEBASE_CONFIG) << "provided value is not a legal IP protocol. Value: " << protocol;
}
}
if (port == "*") {
is_any_port = true;
} else {
is_any_port = false;
try {
value = stoi(port);
if (value > static_cast<int>(UINT16_MAX) || value < 0) {
dbgWarning(D_RULEBASE_CONFIG) << "provided value is not a legal port number. Value: " << port;
} else {
parsed_port = value;
}
} catch (...) {
dbgWarning(D_RULEBASE_CONFIG) << "provided value is not a legal port. Value: " << port;
}
}
if (ip == "*") {
is_any_ip = true;
} else {
is_any_ip = false;
auto ip_addr = IPAddr::createIPAddr(ip);
if (!ip_addr.ok()) {
dbgWarning(D_RULEBASE_CONFIG) << "Could not create IP address. Error: " << ip_addr.getErr();
} else {
parsed_ip = ConvertToIpAddress(ip_addr.unpackMove());
}
}
}
IpAddress
RuleAsset::AssetUrl::ConvertToIpAddress(const IPAddr &addr)
{
IpAddress address;
switch (addr.getType()) {
case IPType::UNINITIALIZED: {
address.addr4_t = {0};
address.ip_type = IP_VERSION_ANY;
break;
}
case IPType::V4: {
address.addr4_t = addr.getIPv4();
address.ip_type = IP_VERSION_4;
break;
}
case IPType::V6: {
address.addr6_t = addr.getIPv6();
address.ip_type = IP_VERSION_6;
break;
}
default:
address.addr4_t = {0};
address.ip_type = IP_VERSION_ANY;
dbgWarning(D_RULEBASE_CONFIG) << "Unsupported IP type: " << static_cast<int>(addr.getType());
}
return address;
}
const Assets Assets::empty_assets_config = Assets();
void
Assets::preload()
{
registerExpectedSetting<Assets>("rulebase", "usedAssets");
}

View File

@@ -0,0 +1 @@
add_library(generic_rulebase_evaluators asset_eval.cc parameter_eval.cc practice_eval.cc query_eval.cc trigger_eval.cc zone_eval.cc connection_eval.cc http_transaction_data_eval.cc)

View File

@@ -0,0 +1,40 @@
// 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 "generic_rulebase/evaluators/asset_eval.h"
#include <vector>
#include <string>
#include "generic_rulebase/assets_config.h"
#include "config.h"
#include "debug.h"
using namespace std;
string AssetMatcher::ctx_key = "asset_id";
AssetMatcher::AssetMatcher(const vector<string> &params)
{
if (params.size() != 1) reportWrongNumberOfParams(AssetMatcher::getName(), params.size(), 1, 1);
asset_id = params[0];
}
Maybe<bool, Context::Error>
AssetMatcher::evalVariable() const
{
I_Environment *env = Singleton::Consume<I_Environment>::by<AssetMatcher>();
auto bc_asset_id_ctx = env->get<GenericConfigId>(AssetMatcher::ctx_key);
return bc_asset_id_ctx.ok() && *bc_asset_id_ctx == asset_id;
}

View File

@@ -0,0 +1,299 @@
// 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 "generic_rulebase/evaluators/connection_eval.h"
#include <vector>
#include <string>
#include "generic_rulebase/rulebase_config.h"
#include "config.h"
#include "debug.h"
#include "ip_utilities.h"
using namespace std;
USE_DEBUG_FLAG(D_RULEBASE_CONFIG);
string IpAddressMatcher::ctx_key = "ipAddress";
string SourceIpMatcher::ctx_key = "sourceIP";
string DestinationIpMatcher::ctx_key = "destinationIP";
string SourcePortMatcher::ctx_key = "sourcePort";
string ListeningPortMatcher::ctx_key = "listeningPort";
string IpProtocolMatcher::ctx_key = "ipProtocol";
string UrlMatcher::ctx_key = "url";
Maybe<IPAddr>
getIpAddrFromEnviroment(I_Environment *env, Context::MetaDataType enum_data_type, const string &str_data_type)
{
auto ip_str = env->get<string>(enum_data_type);
if (!ip_str.ok()) {
dbgWarning(D_RULEBASE_CONFIG) << "Failed to get " << str_data_type << " from the enviroment.";
return genError("Failed to get " + str_data_type + " from the enviroment.");
}
return IPAddr::createIPAddr(ip_str.unpack());
}
bool
checkIfIpInRangesVec(const vector<CustomRange<IPAddr>> &values, const IPAddr &ip_to_check)
{
if (values.size() == 0) {
dbgTrace(D_RULEBASE_CONFIG) << "Ip addersses vector empty. Match is true.";
return true;
}
for (const CustomRange<IPAddr> &range : values) {
if (range.contains(ip_to_check)) {
dbgTrace(D_RULEBASE_CONFIG) << "Ip adderss matched: " << ip_to_check;
return true;
}
}
dbgTrace(D_RULEBASE_CONFIG) << "Ip adderss not match: " << ip_to_check;
return false;
}
IpAddressMatcher::IpAddressMatcher(const vector<string> &params)
{
for (const string &param : params) {
Maybe<CustomRange<IPAddr>> ip_range = CustomRange<IPAddr>::createRange(param);
if (!ip_range.ok()) {
dbgWarning(D_RULEBASE_CONFIG) << "Failed to create ip. Error: " + ip_range.getErr();
continue;
}
values.push_back(ip_range.unpack());
}
}
Maybe<bool, Context::Error>
IpAddressMatcher::evalVariable() const
{
I_Environment *env = Singleton::Consume<I_Environment>::by<IpAddressMatcher>();
Maybe<IPAddr> subject_ip = getIpAddrFromEnviroment(
env,
Context::MetaDataType::SubjectIpAddr,
"subject ip address"
);
if (subject_ip.ok() && checkIfIpInRangesVec(values, subject_ip.unpack())) return true;
Maybe<IPAddr> other_ip = getIpAddrFromEnviroment(
env,
Context::MetaDataType::OtherIpAddr,
"other ip address"
);
if (other_ip.ok() && checkIfIpInRangesVec(values, other_ip.unpack())) return true;
if (!subject_ip.ok() && !other_ip.ok()) {
dbgWarning(D_RULEBASE_CONFIG) << "Error in getting subject ip and other ip from the enviroment";
return false;
}
dbgTrace(D_RULEBASE_CONFIG) << "Ip adderss didn't match";
return false;
}
SourceIpMatcher::SourceIpMatcher(const vector<string> &params)
{
for (const string &param : params) {
Maybe<CustomRange<IPAddr>> ip_range = CustomRange<IPAddr>::createRange(param);
if (!ip_range.ok()) {
dbgWarning(D_RULEBASE_CONFIG) << "Failed to create source ip. Error: " + ip_range.getErr();
continue;
}
values.push_back(ip_range.unpack());
}
}
Maybe<bool, Context::Error>
SourceIpMatcher::evalVariable() const
{
I_Environment *env = Singleton::Consume<I_Environment>::by<SourceIpMatcher>();
auto direction_maybe = env->get<string>(Context::MetaDataType::Direction);
if (!direction_maybe.ok()) {
dbgWarning(D_RULEBASE_CONFIG) << "Failed to get direction from the enviroment.";
return false;
}
string direction = direction_maybe.unpack();
if (direction == "incoming") {
Maybe<IPAddr> other_ip = getIpAddrFromEnviroment(
env,
Context::MetaDataType::OtherIpAddr,
"other ip address"
);
return other_ip.ok() && checkIfIpInRangesVec(values, other_ip.unpack());
} else if (direction == "outgoing") {
Maybe<IPAddr> subject_ip = getIpAddrFromEnviroment(
env,
Context::MetaDataType::SubjectIpAddr,
"subject ip address"
);
return subject_ip.ok() && checkIfIpInRangesVec(values, subject_ip.unpack());
}
dbgTrace(D_RULEBASE_CONFIG) << "Source ip adderss didn't match";
return false;
}
DestinationIpMatcher::DestinationIpMatcher(const vector<string> &params)
{
for (const string &param : params) {
Maybe<CustomRange<IPAddr>> ip_range = CustomRange<IPAddr>::createRange(param);
if (!ip_range.ok()) {
dbgWarning(D_RULEBASE_CONFIG) << "Failed to create destination ip. Error: " + ip_range.getErr();
continue;
}
values.push_back(ip_range.unpack());
}
}
Maybe<bool, Context::Error>
DestinationIpMatcher::evalVariable() const
{
I_Environment *env = Singleton::Consume<I_Environment>::by<DestinationIpMatcher>();
auto direction_maybe = env->get<string>(Context::MetaDataType::Direction);
if (!direction_maybe.ok()) {
dbgWarning(D_RULEBASE_CONFIG) << "Failed to get direction.";
return false;
}
string direction = direction_maybe.unpack();
if (direction == "outgoing") {
Maybe<IPAddr> other_ip = getIpAddrFromEnviroment(
env,
Context::MetaDataType::OtherIpAddr,
"other ip address"
);
return other_ip.ok() && checkIfIpInRangesVec(values, other_ip.unpack());
} else if (direction == "incoming") {
Maybe<IPAddr> subject_ip = getIpAddrFromEnviroment(
env,
Context::MetaDataType::SubjectIpAddr,
"subject ip address"
);
return subject_ip.ok() && checkIfIpInRangesVec(values, subject_ip.unpack());
}
dbgTrace(D_RULEBASE_CONFIG) << "Destination ip adderss didn't match";
return false;
}
SourcePortMatcher::SourcePortMatcher(const vector<string> &params)
{
for (const string &param : params) {
Maybe<CustomRange<PortNumber>> port_range = CustomRange<PortNumber>::createRange(param);
if (!port_range.ok()) {
dbgWarning(D_RULEBASE_CONFIG) << "Failed to create source port.";
continue;
}
values.push_back(port_range.unpack());
}
}
Maybe<bool, Context::Error>
SourcePortMatcher::evalVariable() const
{
dbgTrace(D_RULEBASE_CONFIG) << "Source is not a match";
return false;
}
ListeningPortMatcher::ListeningPortMatcher(const vector<string> &params)
{
for (const string &param : params) {
Maybe<CustomRange<PortNumber>> port_range = CustomRange<PortNumber>::createRange(param);
if (!port_range.ok()) {
dbgWarning(D_RULEBASE_CONFIG) << "Failed to create listening port range.";
continue;
}
values.push_back(port_range.unpack());
}
}
Maybe<bool, Context::Error>
ListeningPortMatcher::evalVariable() const
{
I_Environment *env = Singleton::Consume<I_Environment>::by<ListeningPortMatcher>();
auto port_str = env->get<string>(Context::MetaDataType::Port);
if (!port_str.ok()) {
dbgWarning(D_RULEBASE_CONFIG) << "Failed to get port from the enviroment.";
return false;
}
PortNumber port;
if (ConnKeyUtil::fromString(port_str.unpack(), port)) {
if (values.size() == 0) return true;
for (const CustomRange<PortNumber> &port_range : values) {
if (port_range.contains(port)) {
dbgTrace(D_RULEBASE_CONFIG) << "Listening port is a match. Value: " << port_str.unpack();
return true;
}
}
}
dbgTrace(D_RULEBASE_CONFIG) << "Listening port is not a match. Value: " << port_str.unpack();
return false;
}
IpProtocolMatcher::IpProtocolMatcher(const vector<string> &params)
{
for (const string &param : params) {
Maybe<CustomRange<IPProto>> proto_range = CustomRange<IPProto>::createRange(param);
if (!proto_range.ok()) {
dbgWarning(D_RULEBASE_CONFIG) << "Failed to create ip protocol.";
continue;
}
values.push_back(proto_range.unpack());
}
}
Maybe<bool, Context::Error>
IpProtocolMatcher::evalVariable() const
{
I_Environment *env = Singleton::Consume<I_Environment>::by<IpProtocolMatcher>();
auto proto_str = env->get<string>(Context::MetaDataType::Protocol);
if (!proto_str.ok()) {
dbgWarning(D_RULEBASE_CONFIG) << "Failed to get ip protocol from the enviroment.";
return false;
}
IPProto protocol;
if (ConnKeyUtil::fromString(proto_str.unpack(), protocol)) {
if (values.size() == 0) return true;
for (const CustomRange<IPProto> &proto_range : values) {
if (proto_range.contains(protocol)) {
dbgTrace(D_RULEBASE_CONFIG) << "Ip protocol is a match. Value: " << proto_str.unpack();
return true;
}
}
}
dbgTrace(D_RULEBASE_CONFIG) << "Source port is not a match. Value: " << proto_str.unpack();
return false;
}
UrlMatcher::UrlMatcher(const vector<string> &params) : values(params) {}
Maybe<bool, Context::Error>
UrlMatcher::evalVariable() const
{
I_Environment *env = Singleton::Consume<I_Environment>::by<UrlMatcher>();
auto curr_url_ctx = env->get<string>(Context::MetaDataType::Url);
if (!curr_url_ctx.ok()) {
dbgWarning(D_RULEBASE_CONFIG) << "Failed to get URL from the enviroment.";
return false;
}
if (values.size() == 0) {
dbgTrace(D_RULEBASE_CONFIG) << "Matched URL on \"any\". Url: " << *curr_url_ctx;
return true;
}
for (const string &url : values) {
if (*curr_url_ctx == url) {
dbgTrace(D_RULEBASE_CONFIG) << "Matched URL. Value: " << *curr_url_ctx;
return true;
}
}
dbgTrace(D_RULEBASE_CONFIG) << "URL is not a match. Value: " << *curr_url_ctx;
return false;
}

View File

@@ -0,0 +1,125 @@
// 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 "generic_rulebase/evaluators/http_transaction_data_eval.h"
#include <boost/lexical_cast.hpp>
#include <algorithm>
#include "http_transaction_data.h"
#include "environment/evaluator_templates.h"
#include "i_environment.h"
#include "singleton.h"
#include "sasal.h"
SASAL_START // HTTP Manager - Transaction data
using namespace std;
using namespace EnvironmentHelper;
EqualHost::EqualHost(const vector<string> &params)
{
if (params.size() != 1) reportWrongNumberOfParams("EqualHost", params.size(), 1, 1);
host = params[0];
}
Maybe<bool, Context::Error>
EqualHost::evalVariable() const
{
I_Environment *env = Singleton::Consume<I_Environment>::by<EqualHost>();
auto host_ctx = env->get<string>(HttpTransactionData::host_name_ctx);
if (!host_ctx.ok())
{
return false;
}
std::string lower_host_ctx = host_ctx.unpack();
std::transform(lower_host_ctx.begin(), lower_host_ctx.end(), lower_host_ctx.begin(), ::tolower);
std::string lower_host = host;
std::transform(lower_host.begin(), lower_host.end(), lower_host.begin(), ::tolower);
if (lower_host_ctx == lower_host) return true;
size_t pos = lower_host_ctx.find_last_of(':');
if (pos == string::npos) return false;
lower_host_ctx = string(lower_host_ctx.data(), pos);
return lower_host_ctx == lower_host;
}
EqualListeningIP::EqualListeningIP(const vector<string> &params)
{
if (params.size() != 1) reportWrongNumberOfParams("EqualListeningIP", params.size(), 1, 1);
auto maybe_ip = IPAddr::createIPAddr(params[0]);
if (!maybe_ip.ok()) reportWrongParamType(getName(), params[0], "Not a valid IP Address");
listening_ip = maybe_ip.unpack();
}
Maybe<bool, Context::Error>
EqualListeningIP::evalVariable() const
{
I_Environment *env = Singleton::Consume<I_Environment>::by<EqualListeningIP>();
auto listening_ip_ctx = env->get<IPAddr>(HttpTransactionData::listening_ip_ctx);
return listening_ip_ctx.ok() && listening_ip_ctx.unpack() == listening_ip;
}
EqualListeningPort::EqualListeningPort(const vector<string> &params)
{
if (params.size() != 1) reportWrongNumberOfParams("EqualListeningPort", params.size(), 1, 1);
try {
listening_port = boost::lexical_cast<PortNumber>(params[0]);
} catch (boost::bad_lexical_cast const&) {
reportWrongParamType(getName(), params[0], "Not a valid port number");
}
}
Maybe<bool, Context::Error>
EqualListeningPort::evalVariable() const
{
I_Environment *env = Singleton::Consume<I_Environment>::by<EqualListeningPort>();
auto port_ctx = env->get<PortNumber>(HttpTransactionData::listening_port_ctx);
return port_ctx.ok() && port_ctx.unpack() == listening_port;
}
BeginWithUri::BeginWithUri(const vector<string> &params)
{
if (params.size() != 1) reportWrongNumberOfParams("BeginWithUri", params.size(), 1, 1);
uri_prefix = params[0];
}
Maybe<bool, Context::Error>
BeginWithUri::evalVariable() const
{
I_Environment *env = Singleton::Consume<I_Environment>::by<BeginWithUri>();
auto uri_ctx = env->get<string>(HttpTransactionData::uri_ctx);
if (!uri_ctx.ok())
{
return false;
}
std::string lower_uri_ctx = uri_ctx.unpack();
std::transform(lower_uri_ctx.begin(), lower_uri_ctx.end(), lower_uri_ctx.begin(), ::tolower);
std::string lower_uri_prefix = uri_prefix;
std::transform(lower_uri_prefix.begin(), lower_uri_prefix.end(), lower_uri_prefix.begin(), ::tolower);
return lower_uri_ctx.find(lower_uri_prefix) == 0;
}
SASAL_END

View 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.
#include "generic_rulebase/evaluators/parameter_eval.h"
#include <vector>
#include <string>
#include "generic_rulebase/rulebase_config.h"
#include "config.h"
#include "debug.h"
using namespace std;
string ParameterMatcher::ctx_key = "parameters";
ParameterMatcher::ParameterMatcher(const vector<string> &params)
{
if (params.size() != 1) reportWrongNumberOfParams(ParameterMatcher::getName(), params.size(), 1, 1);
parameter_id = params[0];
}
Maybe<bool, Context::Error>
ParameterMatcher::evalVariable() const
{
auto rule = getConfiguration<BasicRuleConfig>("rulebase", "rulesConfig");
return rule.ok() && rule.unpack().isParameterActive(parameter_id);
}

View File

@@ -0,0 +1,50 @@
// 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 "generic_rulebase/evaluators/practice_eval.h"
#include <vector>
#include <string>
#include "generic_rulebase/rulebase_config.h"
#include "config.h"
#include "debug.h"
USE_DEBUG_FLAG(D_RULEBASE_CONFIG);
using namespace std;
string PracticeMatcher::ctx_key = "practices";
PracticeMatcher::PracticeMatcher(const vector<string> &params)
{
if (params.size() != 1) reportWrongNumberOfParams(PracticeMatcher::getName(), params.size(), 1, 1);
practice_id = params[0];
}
Maybe<bool, Context::Error>
PracticeMatcher::evalVariable() const
{
I_Environment *env = Singleton::Consume<I_Environment>::by<PracticeMatcher>();
auto bc_practice_id_ctx = env->get<set<GenericConfigId>>(PracticeMatcher::ctx_key);
dbgTrace(D_RULEBASE_CONFIG)
<< "Trying to match practice. ID: "
<< practice_id << ", Current set IDs: "
<< makeSeparatedStr(bc_practice_id_ctx.ok() ? *bc_practice_id_ctx : set<GenericConfigId>(), ", ");
if (bc_practice_id_ctx.ok()) {
return bc_practice_id_ctx.unpack().count(practice_id) > 0;
}
auto rule = getConfiguration<BasicRuleConfig>("rulebase", "rulesConfig");
return rule.ok() && rule.unpack().isPracticeActive(practice_id);
}

View File

@@ -0,0 +1,136 @@
// 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 "generic_rulebase/evaluators/query_eval.h"
#include <vector>
#include <string>
#include <map>
#include "generic_rulebase/rulebase_config.h"
#include "generic_rulebase/zones_config.h"
#include "i_environment.h"
#include "singleton.h"
#include "config.h"
#include "debug.h"
#include "enum_range.h"
using namespace std;
USE_DEBUG_FLAG(D_RULEBASE_CONFIG);
QueryMatcher::QueryMatcher(const vector<string> &params)
{
if (params.size() < 1) reportWrongNumberOfParams(QueryMatcher::getName(), params.size(), 1);
key = params.front();
if (key == "any") {
is_any = true;
} else {
values.reserve(params.size() - 1);
for (uint i = 1; i < params.size() ; i++) {
if (params[i] == "any") {
values.clear();
break;
}
values.insert(params[i]);
}
}
}
const string
QueryMatcher::contextKeyToString(Context::MetaDataType type)
{
if (type == Context::MetaDataType::SubjectIpAddr || type == Context::MetaDataType::OtherIpAddr) return "ip";
return Context::convertToString(type);
}
class QueryMatchSerializer
{
public:
static const string req_attr_ctx_key;
template <typename Archive>
void
serialize(Archive &ar)
{
I_Environment *env = Singleton::Consume<I_Environment>::by<QueryMatcher>();
auto req_attr = env->get<string>(req_attr_ctx_key);
if (!req_attr.ok()) return;
try {
ar(cereal::make_nvp(*req_attr, value));
dbgDebug(D_RULEBASE_CONFIG)
<< "Found value for requested attribute. Tag: "
<< *req_attr
<< ", Value: "
<< value;
} catch (exception &e) {
dbgDebug(D_RULEBASE_CONFIG) << "Could not find values for requested attribute. Tag: " << *req_attr;
ar.finishNode();
}
}
template <typename Values>
bool
matchValues(const Values &requested_vals) const
{
return value != "" && (requested_vals.empty() || requested_vals.count(value) > 0);
}
private:
string value;
};
const string QueryMatchSerializer::req_attr_ctx_key = "requested attribute key";
Maybe<bool, Context::Error>
QueryMatcher::evalVariable() const
{
if (is_any) return true;
I_Environment *env = Singleton::Consume<I_Environment>::by<QueryMatcher>();
auto local_asset_ctx = env->get<bool>("is local asset");
bool is_remote_asset = local_asset_ctx.ok() && !(*local_asset_ctx);
QueryRequest request;
for (Context::MetaDataType name : makeRange<Context::MetaDataType>()) {
auto val = env->get<string>(name);
if (val.ok()) {
if ((name == Context::MetaDataType::SubjectIpAddr && is_remote_asset) ||
(name == Context::MetaDataType::OtherIpAddr && !is_remote_asset)) {
continue;
}
request.addCondition(Condition::EQUALS, contextKeyToString(name), *val);
}
}
if (request.empty()) return false;
request.setRequestedAttr(key);
ScopedContext req_attr_key;
req_attr_key.registerValue<string>(QueryMatchSerializer::req_attr_ctx_key, key);
I_Intelligence_IS_V2 *intelligence = Singleton::Consume<I_Intelligence_IS_V2>::by<Zone>();
auto query_res = intelligence->queryIntelligence<QueryMatchSerializer>(request);
if (!query_res.ok()) {
dbgWarning(D_RULEBASE_CONFIG) << "Failed to perform intelligence query. Error: " << query_res.getErr();
return false;
}
for (const AssetReply<QueryMatchSerializer> &asset : query_res.unpack()) {
if (asset.matchValues<unordered_set<string>>(values)) return true;
}
return false;
}

View 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 "generic_rulebase/evaluators/trigger_eval.h"
#include <vector>
#include <string>
#include "generic_rulebase/rulebase_config.h"
#include "config.h"
#include "debug.h"
using namespace std;
USE_DEBUG_FLAG(D_RULEBASE_CONFIG);
string TriggerMatcher::ctx_key = "triggers";
TriggerMatcher::TriggerMatcher(const vector<string> &params)
{
if (params.size() != 1) reportWrongNumberOfParams(TriggerMatcher::getName(), params.size(), 1, 1);
trigger_id = params[0];
}
Maybe<bool, Context::Error>
TriggerMatcher::evalVariable() const
{
I_Environment *env = Singleton::Consume<I_Environment>::by<TriggerMatcher>();
auto ac_bc_trigger_id_ctx = env->get<set<GenericConfigId>>("ac_trigger_id");
dbgTrace(D_RULEBASE_CONFIG)
<< "Trying to match trigger for access control rule. ID: "
<< trigger_id << ", Current set IDs: "
<< makeSeparatedStr(ac_bc_trigger_id_ctx.ok() ? *ac_bc_trigger_id_ctx : set<GenericConfigId>(), ", ");
if (ac_bc_trigger_id_ctx.ok()) {
return ac_bc_trigger_id_ctx.unpack().count(trigger_id) > 0;
}
auto bc_trigger_id_ctx = env->get<set<GenericConfigId>>(TriggerMatcher::ctx_key);
dbgTrace(D_RULEBASE_CONFIG)
<< "Trying to match trigger. ID: "
<< trigger_id << ", Current set IDs: "
<< makeSeparatedStr(bc_trigger_id_ctx.ok() ? *bc_trigger_id_ctx : set<GenericConfigId>(), ", ");
if (bc_trigger_id_ctx.ok() && bc_trigger_id_ctx.unpack().count(trigger_id) > 0 ) return true;
auto rule = getConfiguration<BasicRuleConfig>("rulebase", "rulesConfig");
return rule.ok() && rule.unpack().isTriggerActive(trigger_id);
}

View File

@@ -0,0 +1,44 @@
// 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 "generic_rulebase/evaluators/zone_eval.h"
#include <vector>
#include <string>
#include "generic_rulebase/zone.h"
#include "generic_rulebase/rulebase_config.h"
#include "config.h"
using namespace std;
string ZoneMatcher::ctx_key = "zone_id";
ZoneMatcher::ZoneMatcher(const vector<string> &params)
{
if (params.size() != 1) reportWrongNumberOfParams(ZoneMatcher::getName(), params.size(), 1, 1);
zone_id = params[0];
}
Maybe<bool, Context::Error>
ZoneMatcher::evalVariable() const
{
I_Environment *env = Singleton::Consume<I_Environment>::by<ZoneMatcher>();
auto bc_zone_id_ctx = env->get<GenericConfigId>(ZoneMatcher::ctx_key);
if (bc_zone_id_ctx.ok() && *bc_zone_id_ctx == zone_id) return true;
if (!getProfileAgentSettingWithDefault<bool>(false, "rulebase.enableQueryBasedMatch")) return false;
auto zone = getConfiguration<Zone>("rulebase", "zones");
return zone.ok() && zone.unpack().getId() == zone_id;
}

View File

@@ -0,0 +1,125 @@
// 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 "generic_rulebase/generic_rulebase.h"
#include <unordered_set>
#include "generic_rulebase/evaluators/trigger_eval.h"
#include "generic_rulebase/evaluators/practice_eval.h"
#include "generic_rulebase/evaluators/parameter_eval.h"
#include "generic_rulebase/evaluators/zone_eval.h"
#include "generic_rulebase/evaluators/asset_eval.h"
#include "generic_rulebase/evaluators/query_eval.h"
#include "generic_rulebase/evaluators/connection_eval.h"
#include "generic_rulebase/evaluators/http_transaction_data_eval.h"
#include "generic_rulebase/zone.h"
#include "generic_rulebase/triggers_config.h"
#include "singleton.h"
#include "common.h"
#include "debug.h"
#include "cache.h"
#include "config.h"
using namespace std;
USE_DEBUG_FLAG(D_RULEBASE_CONFIG);
class GenericRulebase::Impl : Singleton::Provide<I_GenericRulebase>::From<GenericRulebase>
{
public:
void init() {}
void fini() {}
void preload();
Maybe<Zone, Config::Errors> getLocalZone() const override { return getZoneConfig(true); }
Maybe<Zone, Config::Errors> getOtherZone() const override { return getZoneConfig(false); }
set<ParameterBehavior> getBehavior(const ParameterKeyValues &key_value_pairs) const override;
private:
Maybe<Zone, Config::Errors>
getZoneConfig(bool is_local_zone) const
{
ScopedContext asset_location_ctx;
asset_location_ctx.registerValue<bool>("is local asset", is_local_zone);
return getConfiguration<Zone>("rulebase", "zones");
}
};
void
GenericRulebase::Impl::preload()
{
addMatcher<TriggerMatcher>();
addMatcher<PracticeMatcher>();
addMatcher<ParameterMatcher>();
addMatcher<ZoneMatcher>();
addMatcher<AssetMatcher>();
addMatcher<QueryMatcher>();
addMatcher<IpAddressMatcher>();
addMatcher<SourceIpMatcher>();
addMatcher<DestinationIpMatcher>();
addMatcher<SourcePortMatcher>();
addMatcher<ListeningPortMatcher>();
addMatcher<IpProtocolMatcher>();
addMatcher<UrlMatcher>();
addMatcher<EqualHost>();
addMatcher<EqualListeningIP>();
addMatcher<EqualListeningPort>();
addMatcher<BeginWithUri>();
BasicRuleConfig::preload();
LogTriggerConf::preload();
ParameterException::preload();
registerExpectedConfiguration<Zone>("rulebase", "zones");
registerExpectedConfigFile("zones", Config::ConfigFileType::Policy);
registerExpectedConfigFile("triggers", Config::ConfigFileType::Policy);
registerExpectedConfigFile("rules", Config::ConfigFileType::Policy);
registerExpectedConfigFile("parameters", Config::ConfigFileType::Policy);
registerExpectedConfigFile("exceptions", Config::ConfigFileType::Policy);
}
set<ParameterBehavior>
GenericRulebase::Impl::getBehavior(const ParameterKeyValues &key_value_pairs) const
{
auto &exceptions = getConfiguration<ParameterException>("rulebase", "exception");
if (!exceptions.ok()) {
dbgTrace(D_RULEBASE_CONFIG) << "Could not find any exception with the current rule's context";
return {};
}
return (*exceptions).getBehavior(key_value_pairs);
}
GenericRulebase::GenericRulebase() : Component("GenericRulebase"), pimpl(make_unique<Impl>()) {}
GenericRulebase::~GenericRulebase() {}
void
GenericRulebase::init()
{
pimpl->init();
}
void
GenericRulebase::fini()
{
pimpl->fini();
}
void
GenericRulebase::preload()
{
pimpl->preload();
}

View File

@@ -0,0 +1,109 @@
// 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 "generic_rulebase/generic_rulebase_context.h"
#include <vector>
#include "context.h"
#include "config.h"
#include "generic_rulebase/evaluators/trigger_eval.h"
#include "generic_rulebase/evaluators/parameter_eval.h"
#include "generic_rulebase/evaluators/practice_eval.h"
#include "generic_rulebase/evaluators/zone_eval.h"
#include "generic_rulebase/evaluators/asset_eval.h"
USE_DEBUG_FLAG(D_RULEBASE_CONFIG);
using namespace std;
template<typename Configs>
set<GenericConfigId>
extractIds(const vector<Configs> &configurations)
{
set<GenericConfigId> ids;
for (const Configs &conf : configurations) {
ids.insert(conf.getId());
}
return ids;
}
void
GenericRulebaseContext::activate(const BasicRuleConfig &rule)
{
switch(registration_state) {
case RuleRegistrationState::UNINITIALIZED: {
registration_state = RuleRegistrationState::REGISTERED;
ctx.registerValue<set<GenericConfigId>>(
TriggerMatcher::ctx_key,
extractIds<RuleTrigger>(rule.getTriggers())
);
ctx.registerValue<set<GenericConfigId>>(
PracticeMatcher::ctx_key,
extractIds<RulePractice>(rule.getPractices())
);
dbgTrace(D_RULEBASE_CONFIG)
<< "Activating current practices. Current practice IDs: "
<< makeSeparatedStr(extractIds<RulePractice>(rule.getPractices()), ", ");
ctx.registerValue<set<GenericConfigId>>(
ParameterMatcher::ctx_key,
extractIds<RuleParameter>(rule.getParameters())
);
ctx.registerValue<GenericConfigId>(
ZoneMatcher::ctx_key,
rule.getZoneId()
);
ctx.registerValue<GenericConfigId>(
AssetMatcher::ctx_key,
rule.getAssetId()
);
ctx.activate();
break;
}
case RuleRegistrationState::REGISTERED: {
dbgTrace(D_RULEBASE_CONFIG) << "Activating registered rule values";
ctx.activate();
break;
}
case RuleRegistrationState::UNREGISTERED: {
dbgTrace(D_RULEBASE_CONFIG) << "Failed to register rule values";
}
}
}
void
GenericRulebaseContext::activate()
{
switch(registration_state) {
case RuleRegistrationState::UNINITIALIZED: {
auto maybe_rule = getConfiguration<BasicRuleConfig>("rulebase", "rulesConfig");
if (!maybe_rule.ok()) {
registration_state = RuleRegistrationState::UNREGISTERED;
return;
}
dbgTrace(D_RULEBASE_CONFIG) << "Registering new rule values";
activate(maybe_rule.unpack());
registration_state = RuleRegistrationState::REGISTERED;
break;
}
case RuleRegistrationState::REGISTERED: {
dbgTrace(D_RULEBASE_CONFIG) << "Activating registered rule values";
ctx.activate();
break;
}
case RuleRegistrationState::UNREGISTERED: {
dbgTrace(D_RULEBASE_CONFIG) << "Failed to register rule values";
}
}
}

View File

@@ -0,0 +1,291 @@
// 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 "generic_rulebase/match_query.h"
#include "cereal/types/set.hpp"
#include "generic_rulebase/generic_rulebase_utils.h"
#include "config.h"
#include "ip_utilities.h"
#include "agent_core_utilities.h"
USE_DEBUG_FLAG(D_RULEBASE_CONFIG);
using namespace std;
static const unordered_map<string, MatchQuery::MatchType> string_to_match_type = {
{ "condition", MatchQuery::MatchType::Condition },
{ "operator", MatchQuery::MatchType::Operator }
};
static const unordered_map<string, MatchQuery::Operators> string_to_operator = {
{ "and", MatchQuery::Operators::And },
{ "or", MatchQuery::Operators::Or }
};
static const unordered_map<string, MatchQuery::Conditions> string_to_condition = {
{ "equals", MatchQuery::Conditions::Equals },
{ "not-equals", MatchQuery::Conditions::NotEquals },
{ "not equals", MatchQuery::Conditions::NotEquals },
{ "in", MatchQuery::Conditions::In },
{ "not-in", MatchQuery::Conditions::NotIn },
{ "not in", MatchQuery::Conditions::NotIn },
{ "exist", MatchQuery::Conditions::Exist }
};
static const string ip_addr_type_name = "IP address";
static const string port_type_name = "port";
static const string ip_proto_type_name = "IP protocol";
static const unordered_map<string, MatchQuery::StaticKeys> string_to_key = {
{ "sourceIP", MatchQuery::StaticKeys::SrcIpAddress },
{ "sourceIpAddr", MatchQuery::StaticKeys::SrcIpAddress },
{ "destinationIP", MatchQuery::StaticKeys::DstIpAddress },
{ "destinationIpAddr", MatchQuery::StaticKeys::DstIpAddress },
{ "ipAddress", MatchQuery::StaticKeys::IpAddress },
{ "sourcePort", MatchQuery::StaticKeys::SrcPort },
{ "listeningPort", MatchQuery::StaticKeys::ListeningPort },
{ "ipProtocol", MatchQuery::StaticKeys::IpProtocol },
{ "domain", MatchQuery::StaticKeys::Domain }
};
void
MatchQuery::load(cereal::JSONInputArchive &archive_in)
{
string type_as_string;
archive_in(cereal::make_nvp("type", type_as_string));
string op_as_string;
archive_in(cereal::make_nvp("op", op_as_string));
auto maybe_type = string_to_match_type.find(type_as_string);
if (maybe_type == string_to_match_type.end()) {
reportConfigurationError("Illegal Zone match query type. Provided type in configuration: " + type_as_string);
}
type = maybe_type->second;
switch (type) {
case (MatchType::Condition): {
auto maybe_condition = string_to_condition.find(op_as_string);
if (maybe_condition == string_to_condition.end()) {
reportConfigurationError(
"Illegal op provided for condition. Provided op in configuration: " +
op_as_string
);
}
condition_type = maybe_condition->second;
operator_type = Operators::None;
archive_in(cereal::make_nvp("key", key));
key_type = getKeyByName(key);
if (key_type == StaticKeys::NotStatic) {
if (key.rfind("containerLabels.", 0) == 0) {
is_specific_label = true;
} else {
is_specific_label = false;
}
}
if (condition_type != Conditions::Exist) {
archive_in(cereal::make_nvp("value", value));
for(const auto &val: value) {
if (isKeyTypeIp()) {
auto ip_range = IPUtilities::createRangeFromString<IPRange, IpAddress>(val, ip_addr_type_name);
if (ip_range.ok()) {
ip_addr_value.push_back(ip_range.unpack());
} else {
dbgWarning(D_RULEBASE_CONFIG)
<< "Failed to parse IP address range. Error: "
<< ip_range.getErr();
}
} else if (isKeyTypePort()) {
auto port_range = IPUtilities::createRangeFromString<PortsRange, uint16_t>(
val,
port_type_name
);
if (port_range.ok()) {
port_value.push_back(port_range.unpack());
} else {
dbgWarning(D_RULEBASE_CONFIG)
<< "Failed to parse port range. Error: "
<< port_range.getErr();
}
} else if (isKeyTypeProtocol()) {
auto proto_range = IPUtilities::createRangeFromString<IpProtoRange, uint8_t>(
val,
ip_proto_type_name
);
if (proto_range.ok()) {
ip_proto_value.push_back(proto_range.unpack());
} else {
dbgWarning(D_RULEBASE_CONFIG)
<< "Failed to parse IP protocol range. Error: "
<< proto_range.getErr();
}
}
try {
regex_values.insert(boost::regex(val));
} catch (const exception &e) {
dbgDebug(D_RULEBASE_CONFIG) << "Failed to compile regex. Error: " << e.what();
}
}
first_value = *(value.begin());
}
break;
}
case (MatchType::Operator): {
auto maybe_operator = string_to_operator.find(op_as_string);
if (maybe_operator == string_to_operator.end()) {
reportConfigurationError(
"Illegal op provided for operator. Provided op in configuration: " +
op_as_string
);
}
operator_type = maybe_operator->second;
condition_type = Conditions::None;
archive_in(cereal::make_nvp("items", items));
break;
}
}
}
MatchQuery::StaticKeys
MatchQuery::getKeyByName(const string &key_type_name)
{
auto key = string_to_key.find(key_type_name);
if (key == string_to_key.end()) return StaticKeys::NotStatic;
return key->second;
}
bool
MatchQuery::isKeyTypeIp() const
{
return (key_type >= StaticKeys::IpAddress && key_type <= StaticKeys::DstIpAddress);
}
bool
MatchQuery::isKeyTypePort() const
{
return (key_type == StaticKeys::SrcPort || key_type == StaticKeys::ListeningPort);
}
bool
MatchQuery::isKeyTypeProtocol() const
{
return (key_type == StaticKeys::IpProtocol);
}
bool
MatchQuery::isKeyTypeDomain() const
{
return (key_type == StaticKeys::Domain);
}
bool
MatchQuery::isKeyTypeSpecificLabel() const
{
return is_specific_label;
}
bool
MatchQuery::isKeyTypeStatic() const
{
return (key_type != StaticKeys::NotStatic);
}
set<string>
MatchQuery::getAllKeys() const
{
set<string> keys;
if (type == MatchType::Condition) {
if (!key.empty()) keys.insert(key);
return keys;
}
for (const MatchQuery &inner_match: items) {
set<string> iner_keys = inner_match.getAllKeys();
keys.insert(iner_keys.begin(), iner_keys.end());
}
return keys;
}
bool
MatchQuery::matchAttributes(const unordered_map<string, set<string>> &key_value_pairs) const
{
if (type == MatchType::Condition) {
auto key_value_pair = key_value_pairs.find(key);
if (key_value_pair == key_value_pairs.end()) {
dbgTrace(D_RULEBASE_CONFIG) << "Ignoring irrelevant key: " << key;
return false;
}
return matchAttributes(key_value_pair->second);
} else if (type == MatchType::Operator && operator_type == Operators::And) {
for (const MatchQuery &inner_match: items) {
if (!inner_match.matchAttributes(key_value_pairs)) return false;
}
return true;
} else if (type == MatchType::Operator && operator_type == Operators::Or) {
for (const MatchQuery &inner_match: items) {
if (inner_match.matchAttributes(key_value_pairs)) return true;
}
return false;
} else {
dbgWarning(D_RULEBASE_CONFIG) << "Unsupported match query type";
}
return false;
}
bool
MatchQuery::matchAttributes(const set<string> &values) const
{
auto &type = condition_type;
bool negate = type == MatchQuery::Conditions::NotEquals || type == MatchQuery::Conditions::NotIn;
bool match = isRegEx() ? matchAttributesRegEx(values) : matchAttributesString(values);
return negate ? !match : match;
}
bool
MatchQuery::matchAttributesRegEx(const set<string> &values) const
{
boost::cmatch value_matcher;
for (const boost::regex &val_regex : regex_values) {
for (const string &requested_match_value : values) {
if (NGEN::Regex::regexMatch(
__FILE__,
__LINE__,
requested_match_value.c_str(),
value_matcher,
val_regex))
{
return true;
}
}
}
return false;
}
bool
MatchQuery::matchAttributesString(const set<string> &values) const
{
for (const string &requested_value : values) {
if (value.find(requested_value) != value.end()) return true;
}
return false;
}
bool
MatchQuery::isRegEx() const
{
return key != "protectionName";
}

View File

@@ -0,0 +1,126 @@
// 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 "generic_rulebase/parameters_config.h"
USE_DEBUG_FLAG(D_RULEBASE_CONFIG);
using namespace std;
bool ParameterException::is_geo_location_exception_exists(false);
bool ParameterException::is_geo_location_exception_being_loaded(false);
void
ParameterOverrides::load(cereal::JSONInputArchive &archive_in)
{
parseJSONKey<vector<ParsedBehavior>>("parsedBehavior", parsed_behaviors, archive_in);
}
void
ParameterTrustedSources::load(cereal::JSONInputArchive &archive_in)
{
parseJSONKey<uint>("numOfSources", num_of_sources, archive_in);
parseJSONKey<vector<SourcesIdentifier>>("sourcesIdentifiers", sources_identidiers, archive_in);
}
void
ParameterBehavior::load(cereal::JSONInputArchive &archive_in)
{
string key_string;
string val_string;
parseJSONKey<string>("id", id, archive_in);
parseJSONKey<string>("key", key_string, archive_in);
parseJSONKey<string>("value", val_string, archive_in);
if (string_to_behavior_key.find(key_string) == string_to_behavior_key.end()) {
dbgWarning(D_RULEBASE_CONFIG) << "Unsupported behavior key: " << key_string;
return;
}
key = string_to_behavior_key.at(key_string);
if (string_to_behavior_val.find(val_string) == string_to_behavior_val.end()) {
dbgWarning(D_RULEBASE_CONFIG) << "Unsupported behavior value: " << val_string;
return;
}
value = string_to_behavior_val.at(val_string);
}
void
ParameterAntiBot::load(cereal::JSONInputArchive &archive_in)
{
parseJSONKey<vector<string>>("injected", injected, archive_in);
parseJSONKey<vector<string>>("validated", validated, archive_in);
}
void
ParameterOAS::load(cereal::JSONInputArchive &archive_in)
{
parseJSONKey<string>("value", value, archive_in);
}
void
ParameterException::MatchBehaviorPair::load(cereal::JSONInputArchive &archive_in)
{
parseJSONKey<MatchQuery>("match", match, archive_in);
parseJSONKey<ParameterBehavior>("behavior", behavior, archive_in);
}
void
ParameterException::load(cereal::JSONInputArchive &archive_in)
{
try {
archive_in(
cereal::make_nvp("match", match),
cereal::make_nvp("behavior", behavior)
);
} catch (...) {
parseJSONKey<vector<MatchBehaviorPair>>("exceptions", match_queries, archive_in);
}
function<bool(const MatchQuery &)> isGeoLocationExists =
[&](const MatchQuery &query)
{
if (query.getKey() == "countryCode" || query.getKey() == "countryName") {
is_geo_location_exception_being_loaded = true;
return true;
}
for (const MatchQuery &query_item : query.getItems()) {
if (isGeoLocationExists(query_item)) return true;
}
return false;
};
if (isGeoLocationExists(match)) return;
for (const MatchBehaviorPair &match_query : match_queries) {
if (isGeoLocationExists(match_query.match)) return;
}
}
set<ParameterBehavior>
ParameterException::getBehavior(const unordered_map<string, set<string>> &key_value_pairs) const
{
set<ParameterBehavior> matched_behaviors;
dbgTrace(D_RULEBASE_CONFIG) << "Matching exception";
for (const MatchBehaviorPair &match_behavior_pair: match_queries) {
if (match_behavior_pair.match.matchAttributes(key_value_pairs)) {
dbgTrace(D_RULEBASE_CONFIG) << "Successfully matched an exception from a list of matches.";
matched_behaviors.insert(match_behavior_pair.behavior);
}
}
if (match_queries.empty() && match.matchAttributes(key_value_pairs)) {
dbgTrace(D_RULEBASE_CONFIG) << "Successfully matched an exception.";
matched_behaviors.insert(behavior);
}
return matched_behaviors;
}

View File

@@ -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.
#include "generic_rulebase/rulebase_config.h"
#include "telemetry.h"
#include "config.h"
USE_DEBUG_FLAG(D_RULEBASE_CONFIG);
using namespace std;
set<string> BasicRuleConfig::assets_ids{};
set<string> BasicRuleConfig::assets_ids_aggregation{};
void
BasicRuleConfig::load(cereal::JSONInputArchive &ar)
{
parseJSONKey<vector<RulePractice>>("practices", practices, ar);
parseJSONKey<vector<RuleTrigger>>("triggers", triggers, ar);
parseJSONKey<vector<RuleParameter>>("parameters", parameters, ar);
parseJSONKey<uint8_t>("priority", priority, ar);
parseJSONKey<string>("ruleId", rule_id, ar);
parseJSONKey<string>("ruleName", rule_name, ar);
parseJSONKey<string>("assetId", asset_id, ar);
parseJSONKey<string>("assetName", asset_name, ar);
parseJSONKey<string>("zoneId", zone_id, ar);
parseJSONKey<string>("zoneName", zone_name, ar);
assets_ids_aggregation.insert(asset_id);
}
void
BasicRuleConfig::updateCountMetric()
{
BasicRuleConfig::assets_ids = BasicRuleConfig::assets_ids_aggregation;
AssetCountEvent(AssetType::ALL, BasicRuleConfig::assets_ids.size()).notify();
}
bool
BasicRuleConfig::isPracticeActive(const string &practice_id) const
{
for (auto practice: practices) {
if (practice.getId() == practice_id) return true;
}
return false;
}
bool
BasicRuleConfig::isTriggerActive(const string &trigger_id) const
{
for (auto trigger: triggers) {
if (trigger.getId() == trigger_id) {
return true;
}
}
return false;
}
bool
BasicRuleConfig::isParameterActive(const string &parameter_id) const
{
for (auto param: parameters) {
if (param.getId() == parameter_id) {
return true;
}
}
return false;
}

View File

@@ -0,0 +1,216 @@
// 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 <string>
#include <map>
#include "generic_rulebase/triggers_config.h"
#include "generic_rulebase/generic_rulebase_utils.h"
USE_DEBUG_FLAG(D_RULEBASE_CONFIG);
using namespace std;
WebTriggerConf::WebTriggerConf() : response_title(""), response_body(""), response_code(0) {}
WebTriggerConf::WebTriggerConf(const string &title, const string &body, uint code)
:
response_title(title),
response_body(body),
response_code(code)
{}
WebTriggerConf WebTriggerConf::default_trigger_conf = WebTriggerConf(
"Attack blocked by web application protection", // title
"Check Point's <b>Application Security</b> has detected an attack and blocked it.", // body
403
);
void
WebTriggerConf::load(cereal::JSONInputArchive &archive_in)
{
try {
parseJSONKey<string>("details level", details_level, archive_in);
if (details_level == "Redirect") {
parseJSONKey<string>("redirect URL", redirect_url, archive_in);
parseJSONKey<bool>("xEventId", add_event_id_to_header, archive_in);
parseJSONKey<bool>("eventIdInHeader", add_event_id_to_header, archive_in);
return;
}
parseJSONKey<uint>("response code", response_code, archive_in);
if (response_code < 100 || response_code > 599) {
throw cereal::Exception(
"illegal web trigger response code: " +
to_string(response_code) +
" is out of range (100-599)"
);
}
if (details_level == "Response Code") return;
parseJSONKey<string>("response body", response_body, archive_in);
parseJSONKey<string>("response title", response_title, archive_in);
} catch (const exception &e) {
dbgWarning(D_RULEBASE_CONFIG) << "Failed to parse the web trigger configuration: '" << e.what() << "'";
archive_in.setNextName(nullptr);
}
}
bool
WebTriggerConf::operator==(const WebTriggerConf &other) const
{
return
response_code == other.response_code &&
response_title == other.response_title &&
response_body == other.response_body;
}
LogTriggerConf::LogTriggerConf(string trigger_name, bool log_detect, bool log_prevent) : name(trigger_name)
{
if (log_detect) should_log_on_detect.setAll();
if (log_prevent) should_log_on_prevent.setAll();
active_streams.setFlag(ReportIS::StreamType::JSON_FOG);
active_streams.setFlag(ReportIS::StreamType::JSON_LOG_FILE);
}
ReportIS::Severity
LogTriggerConf::getSeverity(bool is_action_drop_or_prevent) const
{
return is_action_drop_or_prevent ? ReportIS::Severity::MEDIUM : ReportIS::Severity::LOW;
}
ReportIS::Priority
LogTriggerConf::getPriority(bool is_action_drop_or_prevent) const
{
return is_action_drop_or_prevent ? ReportIS::Priority::HIGH : ReportIS::Priority::MEDIUM;
}
Flags<ReportIS::StreamType>
LogTriggerConf::getStreams(SecurityType security_type, bool is_action_drop_or_prevent) const
{
if (is_action_drop_or_prevent && should_log_on_prevent.isSet(security_type)) return active_streams;
if (!is_action_drop_or_prevent && should_log_on_detect.isSet(security_type)) return active_streams;
return Flags<ReportIS::StreamType>();
}
Flags<ReportIS::Enreachments>
LogTriggerConf::getEnrechments(SecurityType security_type) const
{
Flags<ReportIS::Enreachments> enreachments;
if (log_geo_location.isSet(security_type)) enreachments.setFlag(ReportIS::Enreachments::GEOLOCATION);
if (should_format_output) enreachments.setFlag(ReportIS::Enreachments::BEAUTIFY_OUTPUT);
return enreachments;
}
template <typename EnumClass>
static void
setTriggersFlag(const string &key, cereal::JSONInputArchive &ar, EnumClass flag, Flags<EnumClass> &flags)
{
bool value = false;
parseJSONKey<bool>(key, value, ar);
if (value) flags.setFlag(flag);
}
static void
setLogConfiguration(const ReportIS::StreamType &log_type, const string &log_server_url = "")
{
dbgTrace(D_RULEBASE_CONFIG) << "log server url:" << log_server_url;
if (log_server_url != "") {
Singleton::Consume<I_Logging>::by<LogTriggerConf>()->addStream(log_type, log_server_url);
} else {
Singleton::Consume<I_Logging>::by<LogTriggerConf>()->addStream(log_type);
}
}
void
LogTriggerConf::load(cereal::JSONInputArchive& archive_in)
{
try {
parseJSONKey<string>("triggerName", name, archive_in);
parseJSONKey<string>("verbosity", verbosity, archive_in);
parseJSONKey<string>("urlForSyslog", url_for_syslog, archive_in);
parseJSONKey<string>("urlForCef", url_for_cef, archive_in);
setTriggersFlag("webBody", archive_in, WebLogFields::webBody, log_web_fields);
setTriggersFlag("webHeaders", archive_in, WebLogFields::webHeaders, log_web_fields);
setTriggersFlag("webRequests", archive_in, WebLogFields::webRequests, log_web_fields);
setTriggersFlag("webUrlPath", archive_in, WebLogFields::webUrlPath, log_web_fields);
setTriggersFlag("webUrlQuery", archive_in, WebLogFields::webUrlQuery, log_web_fields);
setTriggersFlag("logToAgent", archive_in, ReportIS::StreamType::JSON_LOG_FILE, active_streams);
setTriggersFlag("logToCloud", archive_in, ReportIS::StreamType::JSON_FOG, active_streams);
setTriggersFlag("logToSyslog", archive_in, ReportIS::StreamType::SYSLOG, active_streams);
setTriggersFlag("logToCef", archive_in, ReportIS::StreamType::CEF, active_streams);
setTriggersFlag("acAllow", archive_in, SecurityType::AccessControl, should_log_on_detect);
setTriggersFlag("acDrop", archive_in, SecurityType::AccessControl, should_log_on_prevent);
setTriggersFlag("tpDetect", archive_in, SecurityType::ThreatPrevention, should_log_on_detect);
setTriggersFlag("tpPrevent", archive_in, SecurityType::ThreatPrevention, should_log_on_prevent);
setTriggersFlag("complianceWarnings", archive_in, SecurityType::Compliance, should_log_on_detect);
setTriggersFlag("complianceViolations", archive_in, SecurityType::Compliance, should_log_on_prevent);
setTriggersFlag("acLogGeoLocation", archive_in, SecurityType::AccessControl, log_geo_location);
setTriggersFlag("tpLogGeoLocation", archive_in, SecurityType::ThreatPrevention, log_geo_location);
setTriggersFlag("complianceLogGeoLocation", archive_in, SecurityType::Compliance, log_geo_location);
bool extend_logging = false;
parseJSONKey<bool>("extendLogging", extend_logging, archive_in);
if (extend_logging) {
setTriggersFlag("responseCode", archive_in, WebLogFields::responseCode, log_web_fields);
setTriggersFlag("responseBody", archive_in, WebLogFields::responseBody, log_web_fields);
string severity;
static const map<string, extendLoggingSeverity> extend_logging_severity_strings = {
{"High", extendLoggingSeverity::High},
{"Critical", extendLoggingSeverity::Critical}
};
parseJSONKey<string>("extendLoggingMinSeverity", severity, archive_in);
auto extended_severity = extend_logging_severity_strings.find(severity);
if (extended_severity != extend_logging_severity_strings.end()) {
extend_logging_severity = extended_severity->second;
} else {
dbgWarning(D_RULEBASE_CONFIG)
<< "Failed to parse the extendLoggingMinSeverityfield: '"
<< severity
<< "'";
}
}
for (ReportIS::StreamType log_stream : makeRange<ReportIS::StreamType>()) {
if (!active_streams.isSet(log_stream)) continue;
switch (log_stream) {
case ReportIS::StreamType::JSON_DEBUG:
setLogConfiguration(ReportIS::StreamType::JSON_DEBUG);
break;
case ReportIS::StreamType::JSON_FOG:
setLogConfiguration(ReportIS::StreamType::JSON_FOG);
break;
case ReportIS::StreamType::JSON_LOG_FILE:
setLogConfiguration(ReportIS::StreamType::JSON_LOG_FILE);
break;
case ReportIS::StreamType::SYSLOG:
setLogConfiguration(ReportIS::StreamType::SYSLOG, getUrlForSyslog());
break;
case ReportIS::StreamType::CEF:
setLogConfiguration(ReportIS::StreamType::CEF, getUrlForCef());
break;
case ReportIS::StreamType::NONE: break;
case ReportIS::StreamType::COUNT: break;
}
}
parseJSONKey<bool>("formatLoggingOutput", should_format_output, archive_in);
} catch (const exception &e) {
dbgWarning(D_RULEBASE_CONFIG) << "Failed to parse the log trigger configuration: '" << e.what() << "'";
archive_in.setNextName(nullptr);
}
}

View File

@@ -0,0 +1,179 @@
// 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 "generic_rulebase/zone.h"
#include <set>
#include <vector>
#include <string>
using namespace std;
static const unordered_map<string, Zone::Direction> string_to_direction = {
{ "to", Zone::Direction::To },
{ "from", Zone::Direction::From },
{ "bidirectional", Zone::Direction::Bidirectional }
};
class AdjacentZone
{
public:
void
load(cereal::JSONInputArchive &archive_in)
{
string direction_as_string;
archive_in(cereal::make_nvp("direction", direction_as_string));
archive_in(cereal::make_nvp("zoneId", id));
auto maybe_direction = string_to_direction.find(direction_as_string);
if (maybe_direction == string_to_direction.end()) {
reportConfigurationError(
"Illegal direction provided for adjacency. Provided direction in configuration: " +
direction_as_string
);
}
dir = maybe_direction->second;
}
pair<Zone::Direction, GenericConfigId> getValue() const { return make_pair(dir, id); }
private:
Zone::Direction dir;
GenericConfigId id;
};
class TagsValues
{
public:
static const string req_attrs_ctx_key;
TagsValues() {}
template <typename Archive>
void
serialize(Archive &ar)
{
I_Environment *env = Singleton::Consume<I_Environment>::by<Zone>();
auto req_attrs = env->get<set<string>>(req_attrs_ctx_key);
if (!req_attrs.ok()) return;
for (const string &req_attr : *req_attrs) {
try {
string data;
ar(cereal::make_nvp(req_attr, data));
dbgDebug(D_RULEBASE_CONFIG)
<< "Found value for requested attribute. Tag: "
<< req_attr
<< ", Value: "
<< data;
tags_set[req_attr].insert(data);
} catch (const exception &e) {
dbgDebug(D_RULEBASE_CONFIG) << "Could not find values for requested attribute. Tag: " << req_attr;
ar.setNextName(nullptr);
}
}
}
bool
matchValueByKey(const string &requested_key, const unordered_set<string> &possible_values) const
{
auto values = tags_set.find(requested_key);
if (values == tags_set.end()) return false;
for (const string &val : possible_values) {
if (values->second.count(val)) return true;
}
return false;
}
void
insert(const TagsValues &other)
{
for (auto &single_tags_value : other.getData()) {
tags_set[single_tags_value.first].insert(single_tags_value.second.begin(), single_tags_value.second.end());
}
}
const unordered_map<string, set<string>> & getData() const { return tags_set; }
private:
unordered_map<string, set<string>> tags_set;
};
const string TagsValues::req_attrs_ctx_key = "requested attributes key";
void
Zone::load(cereal::JSONInputArchive &archive_in)
{
archive_in(cereal::make_nvp("id", zone_id));
archive_in(cereal::make_nvp("name", zone_name));
vector<AdjacentZone> adjacency;
try {
archive_in(cereal::make_nvp("adjacentZones", adjacency));
} catch (const cereal::Exception &) {
dbgTrace(D_RULEBASE_CONFIG)
<< "List of adjacentZones does not exist for current object. Zone id: "
<< zone_id
<< ", Zone name: "
<< zone_name;
archive_in.setNextName(nullptr);
}
for (const AdjacentZone &zone : adjacency) {
adjacent_zones.push_back(zone.getValue());
}
archive_in(cereal::make_nvp("match", match_query));
is_any =
match_query.getType() == MatchQuery::MatchType::Condition &&
match_query.getKey() == "any" &&
match_query.getValue().count("any") > 0;
set<string> keys = match_query.getAllKeys();
}
const string
contextKeyToString(Context::MetaDataType type)
{
if (type == Context::MetaDataType::SubjectIpAddr || type == Context::MetaDataType::OtherIpAddr) return "ip";
return Context::convertToString(type);
}
bool
Zone::contains(const Asset &asset)
{
QueryRequest request;
for (const pair<Context::MetaDataType, string> &main_attr : asset.getAttrs()) {
request.addCondition(Condition::EQUALS, contextKeyToString(main_attr.first), main_attr.second);
}
ScopedContext req_attrs_key;
req_attrs_key.registerValue<set<string>>(TagsValues::req_attrs_ctx_key, match_query.getAllKeys());
I_Intelligence_IS_V2 *intelligence = Singleton::Consume<I_Intelligence_IS_V2>::by<Zone>();
auto query_res = intelligence->queryIntelligence<TagsValues>(request);
if (!query_res.ok()) {
dbgWarning(D_RULEBASE_CONFIG) << "Failed to perform intelligence query. Error: " << query_res.getErr();
return false;
}
for (const AssetReply<TagsValues> &asset : query_res.unpack()) {
TagsValues tag_values = asset.mergeReplyData();
if (match_query.matchAttributes(tag_values.getData())) return true;
}
return false;
}

View File

@@ -0,0 +1,114 @@
// 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 "generic_rulebase/zones_config.h"
#include <string>
#include <unordered_map>
#include "generic_rulebase/generic_rulebase_utils.h"
#include "config.h"
#include "ip_utilities.h"
#include "connkey.h"
#include "i_generic_rulebase.h"
USE_DEBUG_FLAG(D_RULEBASE_CONFIG);
using namespace std;
void
ZonesConfig::load(cereal::JSONInputArchive &archive_in)
{
dbgFlow(D_RULEBASE_CONFIG) << "Saving active zones";
set<string> used_zones;
cereal::load(archive_in, used_zones);
dbgTrace(D_RULEBASE_CONFIG) << "Loading all zones";
auto all_zones_maybe = getSetting<Zones>("rulebase", "zones");
if (!all_zones_maybe.ok()) {
dbgWarning(D_RULEBASE_CONFIG) << "Failed to load zones";
return;
}
dbgTrace(D_RULEBASE_CONFIG) << "Creating cache of all zones by ID";
map<GenericConfigId, Zone> all_zones;
for (const auto &single_zone : all_zones_maybe.unpack().zones) {
if (used_zones.count(single_zone.getId()) > 0 && single_zone.isAnyZone()) {
dbgTrace(D_RULEBASE_CONFIG) << "Found used zone of type \"Any\": saving all zones as active zones";
zones = all_zones_maybe.unpack().zones;
return;
}
dbgWarning(D_RULEBASE_CONFIG)
<< "Adding specific zone to cache. Zone ID: "
<< single_zone.getId()
<< ", name: "
<< single_zone.getName();
all_zones.emplace(single_zone.getId(), single_zone);
}
dbgTrace(D_RULEBASE_CONFIG) << "Creating list of active zones";
map<GenericConfigId, Zone> active_zones_set;
for (const auto &single_used_zone_id : used_zones) {
const auto &found_zone = all_zones[single_used_zone_id];
dbgTrace(D_RULEBASE_CONFIG)
<< "Adding zone to list of active zones. Zone ID: "
<< single_used_zone_id
<< ", zone name: "
<< found_zone.getName();
active_zones_set.emplace(found_zone.getId(), found_zone);
for (const auto &adjacent_zone : found_zone.getAdjacentZones()) {
const auto &adjacent_zone_obj = all_zones[adjacent_zone.second];
dbgTrace(D_RULEBASE_CONFIG)
<< "Adding adjacent zone to list of active zones. Zone ID: "
<< adjacent_zone_obj.getId()
<< ", zone name: "
<< adjacent_zone_obj.getName();
active_zones_set.emplace(adjacent_zone_obj.getId(), adjacent_zone_obj);
}
}
vector<GenericConfigId> implied_zones = {
"impliedAzure",
"impliedDNS",
"impliedSSH",
"impliedProxy",
"impliedFog"
};
GenericConfigId any_zone_id = "";
for (const auto &single_zone : all_zones_maybe.unpack().zones) {
if (single_zone.isAnyZone()) any_zone_id = single_zone.getId();
}
for (GenericConfigId &implied_id: implied_zones) {
if (all_zones.find(implied_id) != all_zones.end()) {
dbgWarning(D_RULEBASE_CONFIG) << "Adding implied zone to cache. Zone ID: " << implied_id;
active_zones_set.emplace(implied_id, all_zones[implied_id]);
if (any_zone_id != "" && active_zones_set.count(any_zone_id) == 0) {
active_zones_set.emplace(any_zone_id, all_zones[any_zone_id]);
}
}
}
for (const auto &single_id_zone_pair : active_zones_set) {
zones.push_back(single_id_zone_pair.second);
}
}
void
ZonesConfig::preload()
{
registerExpectedSetting<Zones>("rulebase", "zones");
registerExpectedSetting<ZonesConfig>("rulebase", "usedZones");
}

View File

@@ -0,0 +1,7 @@
add_definitions(-DUSERSPACE)
include_directories(include)
add_library(gradual_deployment gradual_deployment.cc )
add_subdirectory(gradual_deployment_ut)

View File

@@ -0,0 +1,227 @@
// 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 "gradual_deployment.h"
#include <string>
#include <unordered_map>
#include <boost/algorithm/string.hpp>
#include "enum_range.h"
#include "connkey.h"
USE_DEBUG_FLAG(D_GRADUAL_DEPLOYMENT);
using namespace std;
class SetGradualDeploymentRanges : public ServerRest
{
public:
void doCall() override
{
dbgFlow(D_GRADUAL_DEPLOYMENT) << "Set gradual policy API";
auto maybe_type = convertServiceStrToAttachmentType(attachment_type.get());
if (!maybe_type.ok()) {
string error = "Failed to determine attachment type. Type: "
+ attachment_type.get()
+ ", error: "
+ maybe_type.getErr();
dbgWarning(D_GRADUAL_DEPLOYMENT) << error;
throw JsonError(error);
}
dbgTrace(D_GRADUAL_DEPLOYMENT)
<< "Setting gradual policy for attachment of type: "
<< attachment_type.get();
auto i_gradual_deployment = Singleton::Consume<I_GradualDeployment>::from<GradualDeployment>();
auto set_policy_res = i_gradual_deployment->setPolicy(maybe_type.unpackMove(), ip_ranges.get());
if (!set_policy_res.ok()) throw JsonError(set_policy_res.getErr());
return;
}
private:
C2S_PARAM(vector<string>, ip_ranges)
C2S_PARAM(string, attachment_type)
Maybe<I_GradualDeployment::AttachmentType>
convertServiceStrToAttachmentType(string &type) {
transform(type.begin(), type.end(), type.begin(), ::tolower);
if (type == "http-manager") return I_GradualDeployment::AttachmentType::NGINX;
if (type == "access-control") return I_GradualDeployment::AttachmentType::KERNEL;
return genError("unknown attachment type");
}
};
class GradualDeployment::Impl
:
Singleton::Provide<I_GradualDeployment>::From<GradualDeployment>
{
public:
void
init()
{
dbgFlow(D_GRADUAL_DEPLOYMENT) << "Initializing Gradual Deployment Manager";
auto rest = Singleton::Consume<I_RestApi>::by<GradualDeployment>();
rest->addRestCall<SetGradualDeploymentRanges>(RestAction::SET, "gradual-deployment-policy");
dbgTrace(D_GRADUAL_DEPLOYMENT) << "Gradual Deployment Manager initialization is done successfully";
}
Maybe<void>
setPolicy(I_GradualDeployment::AttachmentType type, const vector<string> &str_ip_ranges) override
{
auto maybe_policy = parseIpRanges(str_ip_ranges);
if (!maybe_policy.ok()) {
auto error = "Failed to set gradual deployment policy. Error: " + maybe_policy.getErr();
dbgWarning(D_GRADUAL_DEPLOYMENT) << error;
return genError(error);
}
ip_ranges_map[static_cast<int>(type)] = maybe_policy.unpackMove();
return Maybe<void>();
}
vector<string>
getPolicy(I_GradualDeployment::AttachmentType type) override
{
vector<string> res;
for (const IPRange &range : ip_ranges_map[static_cast<int>(type)]) {
// Range is validated on insertion
res.push_back(convertIpRangeToStr(range).unpack());
}
return res;
}
vector<IPRange> &
getParsedPolicy(I_GradualDeployment::AttachmentType type) override
{
return ip_ranges_map[static_cast<int>(type)];
}
private:
IpAddress
ConvertToIpAddress(const IPAddr &addr) {
IpAddress address;
switch (addr.getType()) {
case IPType::V4: {
address.addr4_t = addr.getIPv4();
address.ip_type = IP_VERSION_4;
break;
}
case IPType::V6: {
address.addr6_t = addr.getIPv6();
address.ip_type = IP_VERSION_6;
break;
}
default:
dbgAssert(false) << "Unsupported IP type";
}
return address;
}
Maybe<IPRange>
createRangeFromStr(const string &range)
{
vector<string> temp_params_list;
boost::split(temp_params_list, range, boost::is_any_of("-"));
if (temp_params_list.size() == 1) {
Maybe<IPAddr> maybe_ip = IPAddr::createIPAddr(temp_params_list[0]);
if (!maybe_ip.ok()) return genError("Could not create IP address, " + maybe_ip.getErr());
IpAddress addr = move(ConvertToIpAddress(maybe_ip.unpackMove()));
return move(IPRange{.start = addr, .end = addr});
}
if (temp_params_list.size() == 2) {
Maybe<IPAddr> maybe_ip_min = IPAddr::createIPAddr(temp_params_list[0]);
Maybe<IPAddr> maybe_ip_max = IPAddr::createIPAddr(temp_params_list[1]);
if (!maybe_ip_min.ok()) return genError("Could not create IP address, " + maybe_ip_min.getErr());
if (!maybe_ip_max.ok()) return genError("Could not create IP address, " + maybe_ip_max.getErr());
IPAddr min_addr = maybe_ip_min.unpackMove();
IPAddr max_addr = maybe_ip_max.unpackMove();
if (min_addr > max_addr) return genError("Could not create ip range - start greater then end");
IpAddress addr_min = move(ConvertToIpAddress(move(min_addr)));
IpAddress addr_max = move(ConvertToIpAddress(move(max_addr)));
if (addr_max.ip_type != addr_min.ip_type) return genError("Range IP's type does not match");
return move(IPRange{.start = move(addr_min), .end = move(addr_max)});
}
return genError("Illegal range received: " + range);
}
Maybe<vector<IPRange>>
parseIpRanges(const vector<string> &str_ip_ranges)
{
vector<IPRange> ip_ranges;
for (const string &range : str_ip_ranges) {
Maybe<IPRange> ip_range = createRangeFromStr(range);
if (!ip_range.ok()) {
return genError("Failed to parse gradual deployment IP range: " + ip_range.getErr());
}
ip_ranges.push_back(ip_range.unpackMove());
}
return move(ip_ranges);
}
Maybe<string>
convertIpRangeToStr(const IPRange &range)
{
if (range.start.ip_type != IP_VERSION_4 && range.start.ip_type != IP_VERSION_6) {
return genError("Unknown IP type received: " + range.start.ip_type);
}
size_t len;
int type;
const void *in_addr_min;
const void *in_addr_max;
if (range.start.ip_type == IP_VERSION_4) {
len = INET_ADDRSTRLEN;
type = AF_INET;
in_addr_min = &range.start.ip.ipv4;
in_addr_max = &range.end.ip.ipv4;
} else {
len = INET6_ADDRSTRLEN;
type = AF_INET6;
in_addr_min = &range.start.ip.ipv6;
in_addr_max = &range.end.ip.ipv6;
}
char str_min[len];
inet_ntop(type, in_addr_min, str_min, len);
char str_max[len];
inet_ntop(type, in_addr_max, str_max, len);
string start(str_min, strnlen(str_min, len));
string end(str_max, strnlen(str_max, len));
return start + "-" + end;
}
unordered_map<size_t, vector<IPRange>> ip_ranges_map;
};
GradualDeployment::GradualDeployment() : Component("GradualDeployment"), pimpl(make_unique<Impl>()) {}
GradualDeployment::~GradualDeployment() {}
void GradualDeployment::init() { pimpl->init(); }

View File

@@ -0,0 +1,8 @@
include_directories(${Boost_INCLUDE_DIRS})
include_directories(${CMAKE_SOURCE_DIR}/components/include)
add_unit_test(
gradual_deployment_ut
"gradual_deployment_ut.cc"
"singleton;rest;connkey;${RT_LIBRARY};gradual_deployment;"
)

View File

@@ -0,0 +1,127 @@
#include "http_manager.h"
#include <string>
#include <fstream>
#include <streambuf>
#include "cptest.h"
#include "config.h"
#include "singleton.h"
#include "environment.h"
#include "rest_server.h"
#include "table.h"
#include "time_proxy.h"
#include "mainloop.h"
#include "mock/mock_rest_api.h"
#include "i_http_manager.h"
#include "gradual_deployment.h"
using namespace std;
using namespace testing;
class GradualDeploymentTest : public Test
{
public:
GradualDeploymentTest()
{
EXPECT_CALL(rest, mockRestCall(RestAction::SET, "gradual-deployment-policy", _)).WillOnce(
WithArg<2>(Invoke(this, &GradualDeploymentTest::setGDPolicy))
);
gradual_deployment.init();
i_gradual_deployment = Singleton::Consume<I_GradualDeployment>::from(gradual_deployment);
}
bool
setGDPolicy(const unique_ptr<RestInit> &p)
{
gradual_rest_listener = p->getRest();
return true;
}
unique_ptr<ServerRest> gradual_rest_listener;
I_GradualDeployment *i_gradual_deployment;
private:
StrictMock<MockRestApi> rest;
GradualDeployment gradual_deployment;
};
TEST_F(GradualDeploymentTest, getPolicyTest)
{
stringstream is;
is << "{"
<< "\"attachment_type\":\"HTTP-Manager\","
<< "\"ip_ranges\":[\"8.8.8.8\",\"9.9.9.9-10.10.10.10\","
<< "\"0:0:0:0:0:0:0:1-0:0:0:0:0:0:0:4\""
<< "]}";
Maybe<string> rest_call_result = gradual_rest_listener->performRestCall(is);
EXPECT_TRUE(rest_call_result.ok());
vector<string> expected = {"8.8.8.8-8.8.8.8", "9.9.9.9-10.10.10.10", "::1-::4"};
vector<string> curr_policy = i_gradual_deployment->getPolicy(I_GradualDeployment::AttachmentType::NGINX);
EXPECT_EQ(curr_policy, expected);
}
TEST_F(GradualDeploymentTest, MissingAttachmentType)
{
stringstream is("{\"ip_ranges\":[\"8.8\"]}");
Maybe<string> rest_call_result = gradual_rest_listener->performRestCall(is);
EXPECT_FALSE(rest_call_result.ok());
EXPECT_THAT(
rest_call_result.getErr(),
HasSubstr("Couldn't get variable attachment_type")
);
vector<string> expected = {};
vector<string> curr_policy = i_gradual_deployment->getPolicy(I_GradualDeployment::AttachmentType::NGINX);
EXPECT_EQ(curr_policy, expected);
}
TEST_F(GradualDeploymentTest, InvalidAttachmentType)
{
stringstream is;
is << "{"
<< "\"attachment_type\":\"unsupported-attachment-type\","
<< "\"ip_ranges\":[\"8.8.8.8\",\"9.9.9.9-10.10.10.10\","
<< "\"0:0:0:0:0:0:0:1-0:0:0:0:0:0:0:4\""
<< "]}";
Maybe<string> rest_call_result = gradual_rest_listener->performRestCall(is);
EXPECT_FALSE(rest_call_result.ok());
EXPECT_THAT(
rest_call_result.getErr(),
HasSubstr(
"Failed to determine attachment type. "
"Type: unsupported-attachment-type, error: unknown attachment type"
)
);
vector<string> expected = {};
vector<string> curr_policy = i_gradual_deployment->getPolicy(I_GradualDeployment::AttachmentType::NGINX);
EXPECT_EQ(curr_policy, expected);
}
TEST_F(GradualDeploymentTest, InvalidIPRanges)
{
stringstream is;
is << "{"
<< "\"attachment_type\":\"HTTP-Manager\","
<< "\"ip_ranges\":[\"8.8\"]"
<< "}";
Maybe<string> rest_call_result = gradual_rest_listener->performRestCall(is);
EXPECT_FALSE(rest_call_result.ok());
EXPECT_THAT(
rest_call_result.getErr(),
HasSubstr(
"Failed to set gradual deployment policy. "
"Error: Failed to parse gradual deployment IP range: "
"Could not create IP address, String '8.8' is not a valid IPv4/IPv6 address"
)
);
vector<string> expected = {};
vector<string> curr_policy = i_gradual_deployment->getPolicy(I_GradualDeployment::AttachmentType::NGINX);
EXPECT_EQ(curr_policy, expected);
}

View File

@@ -0,0 +1,3 @@
add_library(health_check_manager health_check_manager.cc)
add_subdirectory(health_check_manager_ut)

View File

@@ -0,0 +1,250 @@
// 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_check_manager.h"
#include <fstream>
#include <map>
#include "health_check_status/health_check_status.h"
#include "i_rest_api.h"
#include "config.h"
#include "cereal/archives/json.hpp"
#include "customized_cereal_map.h"
using namespace std;
USE_DEBUG_FLAG(D_HEALTH_CHECK_MANAGER);
class HealthCheckOnDemand : public ServerRest, Singleton::Consume<I_Health_Check_Manager>
{
public:
void
doCall() override
{
string output_path = getProfileAgentSettingWithDefault<string>(
"/tmp/cpnano_health_check_output.txt",
"agent.healthCheck.outputTmpFilePath"
);
ofstream health_check_output_file;
health_check_output_file.open(output_path, ofstream::out | ofstream::trunc);
auto manager = Singleton::Consume<I_Health_Check_Manager>::by<HealthCheckOnDemand>();
manager->printRepliesHealthStatus(health_check_output_file);
health_check_output_file.close();
}
};
class HealthCheckError
{
public:
HealthCheckError(const string &comp_name, const string &error)
:
code_name(comp_name),
is_internal(true)
{
message.push_back(error);
}
template<class Archive>
void
serialize(Archive &ar)
{
ar(
cereal::make_nvp("code", code_name),
cereal::make_nvp("message", message),
cereal::make_nvp("internal", is_internal)
);
}
private:
string code_name;
bool is_internal;
vector<string> message;
};
class HealthCheckValue
{
public:
HealthCheckValue() = default;
HealthCheckValue(HealthCheckStatus raw_status, const map<string, HealthCheckStatusReply> &descriptions)
:
status(raw_status)
{
for (const pair<string, HealthCheckStatusReply> &single_stat : descriptions) {
if (single_stat.second.getStatus() == HealthCheckStatus::HEALTHY) {
dbgTrace(D_HEALTH_CHECK_MANAGER) << "Ignoring healthy status reply. Comp name: " << single_stat.first;
continue;
}
for (const pair<string, string> &status : single_stat.second.getExtendedStatus()) {
errors.push_back(HealthCheckError(single_stat.first + " " + status.first, status.second));
}
}
}
template<class Archive>
void
serialize(Archive &ar)
{
ar(
cereal::make_nvp("status", HealthCheckStatusReply::convertHealthCheckStatusToStr(status)),
cereal::make_nvp("errors", errors)
);
}
private:
HealthCheckStatus status = HealthCheckStatus::IGNORED;
vector<HealthCheckError> errors;
};
class HealthCheckPatch : public ClientRest
{
public:
HealthCheckPatch(HealthCheckStatus raw_status, const map<string, HealthCheckStatusReply> &descriptions)
{
health_check = HealthCheckValue(raw_status, descriptions);
}
C2S_LABEL_PARAM(HealthCheckValue, health_check, "healthCheck");
};
class HealthCheckManager::Impl
:
Singleton::Provide<I_Health_Check_Manager>::From<HealthCheckManager>
{
public:
void
init()
{
auto rest = Singleton::Consume<I_RestApi>::by<HealthCheckManager>();
rest->addRestCall<HealthCheckOnDemand>(RestAction::SHOW, "health-check-on-demand");
int interval_in_seconds =
getProfileAgentSettingWithDefault<int>(30, "agent.healthCheck.intervalInSeconds");
auto i_mainloop = Singleton::Consume<I_MainLoop>::by<HealthCheckManager>();
i_mainloop->addRecurringRoutine(
I_MainLoop::RoutineType::System,
chrono::seconds(interval_in_seconds),
[this]() { executeHealthCheck(); },
"Health check manager periodic check"
);
auto is_orch = Singleton::Consume<I_Environment>::by<HealthCheckManager>()->get<bool>("Is Orchestrator");
should_patch_report = is_orch.ok() && *is_orch;
}
HealthCheckStatus
getAggregatedStatus()
{
executeHealthCheck();
return general_health_aggregated_status;
}
void
printRepliesHealthStatus(ofstream &oputput_file)
{
getRegisteredComponentsHealthStatus();
cereal::JSONOutputArchive ar(oputput_file);
ar(cereal::make_nvp("allComponentsHealthCheckReplies", all_comps_health_status));
}
private:
bool
sendHealthCheckPatch()
{
dbgFlow(D_HEALTH_CHECK_MANAGER);
HealthCheckPatch patch_to_send(general_health_aggregated_status, all_comps_health_status);
auto messaging = Singleton::Consume<I_Messaging>::by<HealthCheckManager>();
return messaging->sendNoReplyObject(patch_to_send, I_Messaging::Method::PATCH, "/agents");
}
void
getRegisteredComponentsHealthStatus()
{
vector<HealthCheckStatusReply> health_check_event_reply = HealthCheckStatusEvent().query();
all_comps_health_status.clear();
for (const auto &reply : health_check_event_reply) {
if (reply.getStatus() != HealthCheckStatus::IGNORED) {
all_comps_health_status.emplace(reply.getCompName(), reply);
}
}
}
void
calcGeneralHealthAggregatedStatus()
{
general_health_aggregated_status = HealthCheckStatus::HEALTHY;
for (const pair<string, HealthCheckStatusReply> &reply : all_comps_health_status) {
HealthCheckStatus status = reply.second.getStatus();
dbgTrace(D_HEALTH_CHECK_MANAGER)
<< "Current aggregated status is: "
<< HealthCheckStatusReply::convertHealthCheckStatusToStr(
general_health_aggregated_status
)
<< ". Got health status: "
<< HealthCheckStatusReply::convertHealthCheckStatusToStr(status)
<< "for component: "
<< reply.first;
switch (status) {
case HealthCheckStatus::UNHEALTHY : {
general_health_aggregated_status = HealthCheckStatus::UNHEALTHY;
return;
}
case HealthCheckStatus::DEGRADED : {
general_health_aggregated_status = HealthCheckStatus::DEGRADED;
break;
}
case HealthCheckStatus::IGNORED : break;
case HealthCheckStatus::HEALTHY : break;
}
}
}
void
executeHealthCheck()
{
dbgFlow(D_HEALTH_CHECK_MANAGER) << "Collecting health status from all registered components.";
getRegisteredComponentsHealthStatus();
calcGeneralHealthAggregatedStatus();
dbgTrace(D_HEALTH_CHECK_MANAGER)
<< "Aggregated status: "
<< HealthCheckStatusReply::convertHealthCheckStatusToStr(general_health_aggregated_status);
if (!should_patch_report) return;
if (!sendHealthCheckPatch()) {
dbgWarning(D_HEALTH_CHECK_MANAGER) << "Failed to send periodic health check patch to the fog";
} else {
dbgDebug(D_HEALTH_CHECK_MANAGER) << "Successfully sent periodic health check patch to the fog";
};
}
HealthCheckStatus general_health_aggregated_status;
map<string, HealthCheckStatusReply> all_comps_health_status;
bool should_patch_report;
};
HealthCheckManager::HealthCheckManager() : Component("HealthCheckManager"), pimpl(make_unique<Impl>()) {}
HealthCheckManager::~HealthCheckManager() {}
void HealthCheckManager::init() { pimpl->init(); }

View File

@@ -0,0 +1,8 @@
include_directories(${CMAKE_SOURCE_DIR}/components/include)
link_directories(${BOOST_ROOT}/lib)
add_unit_test(
health_check_manager_ut
"health_check_manager_ut.cc"
"singleton;mainloop;health_check_manager;event_is;metric;-lboost_regex"
)

View File

@@ -0,0 +1,219 @@
#include "health_check_manager.h"
#include <sstream>
#include <string>
#include <fstream>
#include <chrono>
#include "health_check_status/health_check_status.h"
#include "environment.h"
#include "config.h"
#include "config_component.h"
#include "cptest.h"
#include "mock/mock_mainloop.h"
#include "mock/mock_messaging.h"
#include "mock/mock_rest_api.h"
using namespace std;
using namespace testing;
USE_DEBUG_FLAG(D_HEALTH_CHECK);
class TestHealthCheckStatusListener : public Listener<HealthCheckStatusEvent>
{
public:
void upon(const HealthCheckStatusEvent &) override {}
HealthCheckStatusReply
respond(const HealthCheckStatusEvent &) override
{
map<string, string> extended_status;
extended_status["team"] = team;
extended_status["city"] = city;
HealthCheckStatusReply reply(comp_name, status, extended_status);
return reply;
}
void setStatus(HealthCheckStatus new_status) { status = new_status; }
string getListenerName() const { return "TestHealthCheckStatusListener"; }
private:
static const string comp_name;
HealthCheckStatus status = HealthCheckStatus::HEALTHY;
static const string team;
static const string city;
};
const string TestHealthCheckStatusListener::comp_name = "Test";
const string TestHealthCheckStatusListener::team = "Hapoel";
const string TestHealthCheckStatusListener::city = "Tel-Aviv";
class TestEnd {};
class HealthCheckManagerTest : public Test
{
public:
HealthCheckManagerTest()
{
Debug::setNewDefaultStdout(&debug_output);
Debug::setUnitTestFlag(D_HEALTH_CHECK, Debug::DebugLevel::INFO);
EXPECT_CALL(mock_ml, addRecurringRoutine(_, _, _, _, _)).WillRepeatedly(
DoAll(SaveArg<2>(&health_check_periodic_routine), Return(1))
);
EXPECT_CALL(mock_rest, mockRestCall(RestAction::ADD, "declare-boolean-variable", _)).WillOnce(Return(true));
EXPECT_CALL(mock_rest, mockRestCall(RestAction::SHOW, "health-check-on-demand", _)).WillOnce(
WithArg<2>(Invoke(this, &HealthCheckManagerTest::setHealthCheckOnDemand))
);
env.preload();
event_listener.registerListener();
env.init();
ScopedContext ctx;
ctx.registerValue<bool>("Is Orchestrator", true);
health_check_manager.init();
i_health_check_manager = Singleton::Consume<I_Health_Check_Manager>::from(health_check_manager);
}
~HealthCheckManagerTest()
{
env.fini();
Debug::setNewDefaultStdout(&cout);
}
bool
setHealthCheckOnDemand(const unique_ptr<RestInit> &rest_ptr)
{
health_check_server = rest_ptr->getRest();
return true;
}
I_MainLoop::Routine health_check_periodic_routine;
StrictMock<MockMainLoop> mock_ml;
StrictMock<MockRestApi> mock_rest;
StrictMock<MockMessaging> mock_message;
stringstream debug_output;
ConfigComponent config;
Config::I_Config *i_config = nullptr;
::Environment env;
HealthCheckManager health_check_manager;
I_Health_Check_Manager *i_health_check_manager;
unique_ptr<ServerRest> health_check_server;
TestHealthCheckStatusListener event_listener;
};
TEST_F(HealthCheckManagerTest, runPeriodicHealthCheckTest)
{
string actual_body;
EXPECT_CALL(
mock_message,
sendMessage(
false,
_,
I_Messaging::Method::PATCH,
"/agents",
"",
_,
_,
MessageTypeTag::GENERIC
)
).Times(4).WillRepeatedly(DoAll(SaveArg<1>(&actual_body), Return(string())));
try {
health_check_periodic_routine();
} catch (const TestEnd &t) {}
HealthCheckStatus aggregated_status = i_health_check_manager->getAggregatedStatus();
string aggregated_status_str = HealthCheckStatusReply::convertHealthCheckStatusToStr(aggregated_status);
string expected_healthy_body(
"{\n"
" \"healthCheck\": {\n"
" \"status\": \"Healthy\",\n"
" \"errors\": []\n"
" }\n"
"}"
);
EXPECT_EQ(actual_body, expected_healthy_body);
EXPECT_EQ("Healthy", aggregated_status_str);
event_listener.setStatus(HealthCheckStatus::DEGRADED);
try {
health_check_periodic_routine();
} catch (const TestEnd &t) {}
aggregated_status = i_health_check_manager->getAggregatedStatus();
aggregated_status_str = HealthCheckStatusReply::convertHealthCheckStatusToStr(aggregated_status);
string expected_degraded_body(
"{\n"
" \"healthCheck\": {\n"
" \"status\": \"Degraded\",\n"
" \"errors\": [\n"
" {\n"
" \"code\": \"Test city\",\n"
" \"message\": [\n"
" \"Tel-Aviv\"\n"
" ],\n"
" \"internal\": true\n"
" },\n"
" {\n"
" \"code\": \"Test team\",\n"
" \"message\": [\n"
" \"Hapoel\"\n"
" ],\n"
" \"internal\": true\n"
" }\n"
" ]\n"
" }\n"
"}"
);
EXPECT_EQ(actual_body, expected_degraded_body);
EXPECT_EQ("Degraded", aggregated_status_str);
}
TEST_F(HealthCheckManagerTest, runOnDemandHealthCheckTest)
{
const vector<string> health_check{""};
CPTestTempfile health_check_tmp_file(health_check);
string config_json =
"{"
" \"agentSettings\": [\n"
" {\n"
" \"id\": \"yallaHapoel\",\n"
" \"key\": \"agent.healthCheck.outputTmpFilePath\",\n"
" \"value\": \"" + health_check_tmp_file.fname + "\"\n"
" }]\n"
"}";
istringstream ss(config_json);
config.preload();
Singleton::Consume<Config::I_Config>::from(config)->loadConfiguration(ss);
stringstream is;
is << "{}";
health_check_server->performRestCall(is);
string expected_status =
"{\n"
" \"allComponentsHealthCheckReplies\": {\n"
" \"Test\": {\n"
" \"status\": \"Healthy\",\n"
" \"extendedStatus\": {\n"
" \"city\": \"Tel-Aviv\",\n"
" \"team\": \"Hapoel\"\n"
" }\n"
" }\n"
" }\n"
"}";
string health_check_res = health_check_tmp_file.readFile();
EXPECT_EQ(health_check_res, expected_status);
}

View File

@@ -0,0 +1 @@
add_library(http_manager_comp http_manager.cc http_manager_opaque.cc )

View File

@@ -0,0 +1,379 @@
// 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_manager.h"
#include <string>
#include <map>
#include <sys/stat.h>
#include <climits>
#include <unordered_map>
#include <boost/range/iterator_range.hpp>
#include <fstream>
#include <algorithm>
#include "common.h"
#include "config.h"
#include "table_opaque.h"
#include "http_manager_opaque.h"
#include "log_generator.h"
#include "sasal.h"
#include "http_inspection_events.h"
SASAL_START // HTTP Manager
USE_DEBUG_FLAG(D_HTTP_MANAGER);
using namespace std;
static ostream &
operator<<(ostream &os, const EventVerdict &event)
{
switch (event.getVerdict()) {
case ngx_http_cp_verdict_e::TRAFFIC_VERDICT_INSPECT: return os << "Inspect";
case ngx_http_cp_verdict_e::TRAFFIC_VERDICT_ACCEPT: return os << "Accept";
case ngx_http_cp_verdict_e::TRAFFIC_VERDICT_DROP: return os << "Drop";
case ngx_http_cp_verdict_e::TRAFFIC_VERDICT_INJECT: return os << "Inject";
case ngx_http_cp_verdict_e::TRAFFIC_VERDICT_IRRELEVANT: return os << "Irrelevant";
case ngx_http_cp_verdict_e::TRAFFIC_VERDICT_RECONF: return os << "Reconf";
case ngx_http_cp_verdict_e::TRAFFIC_VERDICT_WAIT: return os << "Wait";
}
dbgAssert(false) << "Illegal Event Verdict value: " << static_cast<uint>(event.getVerdict());
return os;
}
class HttpManager::Impl
:
Singleton::Provide<I_HttpManager>::From<HttpManager>
{
public:
void
init()
{
dbgFlow(D_HTTP_MANAGER);
i_transaction_table = Singleton::Consume<I_Table>::by<HttpManager>();
Singleton::Consume<I_Logging>::by<HttpManager>()->addGeneralModifier(compressAppSecLogs);
}
FilterVerdict
inspect(const HttpTransactionData &event) override
{
if (!i_transaction_table->createState<HttpManagerOpaque>()) {
dbgWarning(D_HTTP_MANAGER) << "Failed to create new transaction table state - Returning default verdict.";
return FilterVerdict(default_verdict);
}
ScopedContext ctx;
ctx.registerValue(app_sec_marker_key, i_transaction_table->keyToString(), EnvKeyAttr::LogSection::MARKER);
return handleEvent(NewHttpTransactionEvent(event).performNamedQuery());
}
FilterVerdict
inspect(const HttpHeader &event, bool is_request) override
{
if (!i_transaction_table->hasState<HttpManagerOpaque>()) {
dbgWarning(D_HTTP_MANAGER) << "Transaction state was not found - Returning default verdict.";
return FilterVerdict(default_verdict);
}
ScopedContext ctx;
ctx.registerValue(app_sec_marker_key, i_transaction_table->keyToString(), EnvKeyAttr::LogSection::MARKER);
auto event_responds =
is_request ?
HttpRequestHeaderEvent(event).performNamedQuery() :
HttpResponseHeaderEvent(event).performNamedQuery();
FilterVerdict verdict = handleEvent(event_responds);
if (verdict.getVerdict() == ngx_http_cp_verdict_e::TRAFFIC_VERDICT_INJECT) {
applyInjectionModifications(verdict, event_responds, event.getHeaderIndex());
}
return verdict;
}
FilterVerdict
inspect(const HttpBody &event, bool is_request) override
{
if (!i_transaction_table->hasState<HttpManagerOpaque>()) {
dbgWarning(D_HTTP_MANAGER) << "Transaction state was not found - Returning default verdict.";
return FilterVerdict(default_verdict);
}
ngx_http_cp_verdict_e body_size_limit_verdict = handleBodySizeLimit(is_request, event);
if (body_size_limit_verdict != ngx_http_cp_verdict_e::TRAFFIC_VERDICT_INSPECT) {
return FilterVerdict(body_size_limit_verdict);
}
HttpManagerOpaque &state = i_transaction_table->getState<HttpManagerOpaque>();
ScopedContext ctx;
ctx.registerValue(app_sec_marker_key, i_transaction_table->keyToString(), EnvKeyAttr::LogSection::MARKER);
FilterVerdict verdict(ngx_http_cp_verdict_e::TRAFFIC_VERDICT_INSPECT);
if (!is_request && event.getData().size() == 0 && !event.isLastChunk()) {
dbgDebug(D_HTTP_MANAGER) << "Skipping inspection of first empty chunk for respond body";
return verdict;
}
auto event_responds =
is_request ?
HttpRequestBodyEvent(event, state.getPreviousDataCache()).performNamedQuery() :
HttpResponseBodyEvent(event, state.getPreviousDataCache()).performNamedQuery();
verdict = handleEvent(event_responds);
state.saveCurrentDataToCache(event.getData());
if (verdict.getVerdict() == ngx_http_cp_verdict_e::TRAFFIC_VERDICT_INJECT) {
applyInjectionModifications(verdict, event_responds, event.getBodyChunkIndex());
}
return verdict;
}
FilterVerdict
inspect(const ResponseCode &event) override
{
if (!i_transaction_table->hasState<HttpManagerOpaque>()) {
dbgWarning(D_HTTP_MANAGER) << "Transaction state was not found - Returning default verdict.";
return FilterVerdict(default_verdict);
}
ScopedContext ctx;
ctx.registerValue(app_sec_marker_key, i_transaction_table->keyToString(), EnvKeyAttr::LogSection::MARKER);
return handleEvent(ResponseCodeEvent(event).performNamedQuery());
}
FilterVerdict
inspectEndRequest() override
{
if (!i_transaction_table->hasState<HttpManagerOpaque>()) {
dbgWarning(D_HTTP_MANAGER) << "Transaction state was not found - Returning default verdict.";
return FilterVerdict(default_verdict);
}
HttpManagerOpaque &state = i_transaction_table->getState<HttpManagerOpaque>();
state.resetPayloadSize();
ScopedContext ctx;
ctx.registerValue(app_sec_marker_key, i_transaction_table->keyToString(), EnvKeyAttr::LogSection::MARKER);
return handleEvent(EndRequestEvent().performNamedQuery());
}
FilterVerdict
inspectEndTransaction() override
{
if (!i_transaction_table->hasState<HttpManagerOpaque>()) {
dbgWarning(D_HTTP_MANAGER) << "Transaction state was not found - Returning default verdict.";
return FilterVerdict(default_verdict);
}
HttpManagerOpaque &state = i_transaction_table->getState<HttpManagerOpaque>();
state.resetPayloadSize();
ScopedContext ctx;
ctx.registerValue(app_sec_marker_key, i_transaction_table->keyToString(), EnvKeyAttr::LogSection::MARKER);
return handleEvent(EndTransactionEvent().performNamedQuery());
}
FilterVerdict
inspectDelayedVerdict() override
{
if (!i_transaction_table->hasState<HttpManagerOpaque>()) {
dbgWarning(D_HTTP_MANAGER) << "Transaction state was not found - Returning default verdict.";
return FilterVerdict(default_verdict);
}
ScopedContext ctx;
ctx.registerValue(app_sec_marker_key, i_transaction_table->keyToString(), EnvKeyAttr::LogSection::MARKER);
return handleEvent(WaitTransactionEvent().performNamedQuery());
}
void
sendPolicyLog()
{
LogGen(
"Web AppSec Policy Loaded Successfully",
ReportIS::Audience::SECURITY,
ReportIS::Severity::LOW,
ReportIS::Priority::LOW,
ReportIS::Tags::THREAT_PREVENTION
);
}
private:
ngx_http_cp_verdict_e
handleBodySizeLimit(bool is_request_body_type, const HttpBody &event)
{
HttpManagerOpaque &state = i_transaction_table->getState<HttpManagerOpaque>();
state.updatePayloadSize(event.getData().size());
auto size_limit = getConfiguration<uint>(
"HTTP manager",
is_request_body_type ? "Max Request Body Size" : "Max Response Body Size"
);
string size_limit_verdict = getConfigurationWithDefault<string>(
"Accept",
"HTTP manager",
is_request_body_type ? "Request Size Limit Verdict" : "Response Size Limit Verdict"
);
if (!size_limit.ok() || state.getAggeregatedPayloadSize() < size_limit.unpack()) {
return ngx_http_cp_verdict_e::TRAFFIC_VERDICT_INSPECT;
}
ngx_http_cp_verdict_e verdict = size_limit_verdict == "Drop" ?
ngx_http_cp_verdict_e::TRAFFIC_VERDICT_DROP :
ngx_http_cp_verdict_e::TRAFFIC_VERDICT_ACCEPT;
dbgDebug(D_HTTP_MANAGER)
<< "Transaction body size is over the limit. Max body size: "
<< size_limit.unpack()
<< ", Returned verdict: "
<< size_limit_verdict
<< ".";
state.setManagerVerdict(verdict);
return verdict;
}
static void
applyInjectionModifications(
FilterVerdict &verdict,
const vector<pair<string, EventVerdict>> &event_responds,
ModifiedChunkIndex event_idx)
{
for (const pair<string, EventVerdict> &respond : event_responds) {
if (respond.second.getVerdict() == ngx_http_cp_verdict_e::TRAFFIC_VERDICT_INJECT) {
dbgTrace(D_HTTP_MANAGER)
<< "Applying inject verdict modifications for security App: "
<< respond.first;
verdict.addModifications(respond.second.getModifications(), event_idx);
}
}
}
FilterVerdict
handleEvent(const vector<pair<string, EventVerdict>> &event_responds)
{
HttpManagerOpaque &state = i_transaction_table->getState<HttpManagerOpaque>();
for (const pair<string, EventVerdict> &respond : event_responds) {
if (state.getApplicationsVerdict(respond.first) == ngx_http_cp_verdict_e::TRAFFIC_VERDICT_ACCEPT) {
dbgTrace(D_HTTP_MANAGER)
<< "Skipping event verdict for app that already accepted traffic. App: "
<< respond.first;
continue;
}
dbgTrace(D_HTTP_MANAGER)
<< "Security app "
<< respond.first
<< " returned verdict "
<< respond.second.getVerdict();
state.setApplicationVerdict(respond.first, respond.second.getVerdict());
}
return state.getCurrVerdict();
}
static void
compressAppSecLogs(LogBulkRest &bulk)
{
dbgTrace(D_HTTP_MANAGER) << "Starting to reduce logs";
map<string, uint> app_sec_logs_by_key;
for (const auto &log : bulk) {
auto &markers = log.getMarkers();
auto appsec_marker = markers.find(app_sec_marker_key);
if (appsec_marker != markers.end()) app_sec_logs_by_key[appsec_marker->second]++;
}
for (const auto &specific_set_of_logs : app_sec_logs_by_key) {
if (specific_set_of_logs.second > 1) reduceLogs(bulk, specific_set_of_logs.first);
}
dbgTrace(D_HTTP_MANAGER) << "Finished logs reduction";
}
static void
reduceLogs(LogBulkRest &bulk, const string &current_id)
{
dbgTrace(D_HTTP_MANAGER) << "Reducing logs for marker " << current_id;
vector<vector<Report>::iterator> relevent_logs;
vector<Report>::iterator keep_log = bulk.end();
for (auto curr_log = bulk.begin(); curr_log != bulk.end(); ++curr_log) {
if (isRelevantLog(curr_log, current_id)) {
relevent_logs.push_back(curr_log);
if (keep_log == bulk.end() || (isPreventLog(curr_log) && !isPreventLog(keep_log))) keep_log = curr_log;
}
}
dbgTrace(D_HTTP_MANAGER) << "Found " << relevent_logs.size() << " logs that match marker " << current_id;
// Reverse iteration to avoid iterator invalidation
for (auto iter = relevent_logs.rbegin(); iter != relevent_logs.rend(); ++iter) {
if (*iter != keep_log) bulk.erase(*iter);
}
dbgTrace(D_HTTP_MANAGER) << "Finished going over maker " << current_id;
}
static bool
isRelevantLog(const vector<Report>::iterator &log, const string &current_id)
{
const auto &markers = log->getMarkers();
auto app_sec_marker = markers.find(app_sec_marker_key);
if (app_sec_marker == markers.end()) return false;
return app_sec_marker->second == current_id;
}
static bool
isPreventLog(const vector<Report>::iterator &log)
{
auto res = log->getStringData("securityAction");
return res.ok() && *res == "Prevent";
}
I_Table *i_transaction_table;
static const ngx_http_cp_verdict_e default_verdict;
static const string app_sec_marker_key;
};
const ngx_http_cp_verdict_e HttpManager::Impl::default_verdict(ngx_http_cp_verdict_e::TRAFFIC_VERDICT_DROP);
const string HttpManager::Impl::app_sec_marker_key = "app_sec_marker";
HttpManager::HttpManager() : Component("HttpManager"), pimpl(make_unique<Impl>()) {}
HttpManager::~HttpManager() {}
void HttpManager::init() { pimpl->init(); }
void
HttpManager::preload()
{
registerExpectedConfiguration<uint>("HTTP manager", "Previous Buffer Cache size");
registerExpectedConfiguration<uint>("HTTP manager", "Max Request Body Size");
registerExpectedConfiguration<uint>("HTTP manager", "Max Response Body Size");
registerExpectedConfiguration<string>("HTTP manager", "Request Size Limit Verdict");
registerExpectedConfiguration<string>("HTTP manager", "Response Size Limit Verdict");
registerConfigLoadCb([this] () { pimpl->sendPolicyLog(); });
}
SASAL_END

View File

@@ -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.
#include "http_manager_opaque.h"
#include "config.h"
#include "sasal.h"
SASAL_START // HTTP Manager - Transaction data
using namespace std;
USE_DEBUG_FLAG(D_HTTP_MANAGER);
HttpManagerOpaque::HttpManagerOpaque()
:
TableOpaqueSerialize<HttpManagerOpaque>(this),
prev_data_cache()
{
}
void
HttpManagerOpaque::setApplicationVerdict(const string &app_name, ngx_http_cp_verdict_e verdict)
{
applications_verdicts[app_name] = verdict;
}
ngx_http_cp_verdict_e
HttpManagerOpaque::getApplicationsVerdict(const string &app_name) const
{
auto verdict = applications_verdicts.find(app_name);
return verdict == applications_verdicts.end() ? ngx_http_cp_verdict_e::TRAFFIC_VERDICT_INSPECT : verdict->second;
}
ngx_http_cp_verdict_e
HttpManagerOpaque::getCurrVerdict() const
{
if (manager_verdict == ngx_http_cp_verdict_e::TRAFFIC_VERDICT_DROP) {
return manager_verdict;
}
uint accepted_apps = 0;
ngx_http_cp_verdict_e verdict = ngx_http_cp_verdict_e::TRAFFIC_VERDICT_INSPECT;
for (const pair<string, ngx_http_cp_verdict_e> &app_verdic_pair : applications_verdicts) {
switch (app_verdic_pair.second) {
case ngx_http_cp_verdict_e::TRAFFIC_VERDICT_DROP:
return app_verdic_pair.second;
case ngx_http_cp_verdict_e::TRAFFIC_VERDICT_INJECT:
verdict = ngx_http_cp_verdict_e::TRAFFIC_VERDICT_INJECT;
break;
case ngx_http_cp_verdict_e::TRAFFIC_VERDICT_ACCEPT:
accepted_apps++;
break;
case ngx_http_cp_verdict_e::TRAFFIC_VERDICT_INSPECT:
break;
case ngx_http_cp_verdict_e::TRAFFIC_VERDICT_IRRELEVANT:
dbgTrace(D_HTTP_MANAGER) << "Verdict 'Irrelevant' is not yet supported. Returning Accept";
accepted_apps++;
break;
case ngx_http_cp_verdict_e::TRAFFIC_VERDICT_WAIT:
verdict = ngx_http_cp_verdict_e::TRAFFIC_VERDICT_WAIT;
break;
default:
dbgAssert(false)
<< "Received unknown verdict "
<< static_cast<int>(app_verdic_pair.second);
}
}
return accepted_apps == applications_verdicts.size() ? ngx_http_cp_verdict_e::TRAFFIC_VERDICT_ACCEPT : verdict;
}
void
HttpManagerOpaque::saveCurrentDataToCache(const Buffer &full_data)
{
uint data_cache_size = getConfigurationWithDefault<uint>(0, "HTTP manager", "Previous Buffer Cache size");
if (data_cache_size == 0) {
prev_data_cache.clear();
return;
}
prev_data_cache = full_data.getSubBuffer(
full_data.size() <= data_cache_size ? 0 : full_data.size() - data_cache_size,
full_data.size()
);
}
void
HttpManagerOpaque::updatePayloadSize(const uint curr_payload_size)
{
aggregated_payload_size += curr_payload_size;
}
SASAL_END

View File

@@ -0,0 +1,55 @@
// 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_MANAGER_OPAQUE_H__
#define __HTTP_MANAGER_OPAQUE_H__
#include <unordered_map>
#include "buffer.h"
#include "table_opaque.h"
#include "nginx_attachment_common.h"
class HttpManagerOpaque : public TableOpaqueSerialize<HttpManagerOpaque>
{
public:
HttpManagerOpaque();
void setApplicationVerdict(const std::string &app_name, ngx_http_cp_verdict_e verdict);
ngx_http_cp_verdict_e getApplicationsVerdict(const std::string &app_name) const;
void setManagerVerdict(ngx_http_cp_verdict_e verdict) { manager_verdict = verdict; }
ngx_http_cp_verdict_e getManagerVerdict() const { return manager_verdict; }
ngx_http_cp_verdict_e getCurrVerdict() const;
void saveCurrentDataToCache(const Buffer &full_data);
const Buffer & getPreviousDataCache() const { return prev_data_cache; }
uint getAggeregatedPayloadSize() const { return aggregated_payload_size; }
void updatePayloadSize(const uint curr_payload);
void resetPayloadSize() { aggregated_payload_size = 0; }
// LCOV_EXCL_START - sync functions, can only be tested once the sync module exists
template <typename T> void serialize(T &ar, uint) { ar(applications_verdicts, prev_data_cache); }
static std::unique_ptr<TableOpaqueBase> prototype() { return std::make_unique<HttpManagerOpaque>(); }
// LCOV_EXCL_STOP
static const std::string name() { return "HttpTransactionData"; }
static uint currVer() { return 0; }
static uint minVer() { return 0; }
private:
std::unordered_map<std::string, ngx_http_cp_verdict_e> applications_verdicts;
ngx_http_cp_verdict_e manager_verdict = ngx_http_cp_verdict_e::TRAFFIC_VERDICT_INSPECT;
Buffer prev_data_cache;
uint aggregated_payload_size = 0;
};
#endif // __HTTP_MANAGER_OPAQUE_H__

View File

@@ -0,0 +1,5 @@
add_definitions(-DUSERSPACE)
add_library(http_transaction_data http_transaction_data.cc)
add_subdirectory(http_transaction_data_ut)

View File

@@ -0,0 +1,265 @@
// 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_transaction_data.h"
#include <sys/time.h>
#include <sstream>
#include <boost/algorithm/string.hpp>
#include "enum_array.h"
#include "sasal.h"
#include "buffer.h"
#include "nginx_attachment_common.h"
SASAL_START // HTTP Manager - Transaction data
using namespace std;
USE_DEBUG_FLAG(D_NGINX_ATTACHMENT);
enum class ETransactionData {
HTTP_PROTO,
METHOD,
HOST_NAME,
LISTENING_IP,
LISTENING_PORT,
URI,
CLIENT_IP,
CLIENT_PORT,
COUNT
};
const string HttpTransactionData::http_proto_ctx = "transaction_http_proto";
const string HttpTransactionData::method_ctx = "transaction_method";
const string HttpTransactionData::host_name_ctx = "transaction_host_name";
const string HttpTransactionData::listening_ip_ctx = "transaction_listening_ip";
const string HttpTransactionData::listening_port_ctx = "transaction_listening_port";
const string HttpTransactionData::uri_ctx = "transaction_uri";
const string HttpTransactionData::uri_path_decoded = "transaction_uri_path_decoded";
const string HttpTransactionData::uri_query_decoded = "transaction_uri_query_decoded";
const string HttpTransactionData::client_ip_ctx = "transaction_client_ip";
const string HttpTransactionData::client_port_ctx = "transaction_client_port";
const string HttpTransactionData::req_headers = "transaction_request_headers";
const string HttpTransactionData::req_body = "transaction_request_body";
const string HttpTransactionData::source_identifier = "sourceIdentifiers";
const string HttpTransactionData::proxy_ip_ctx = "proxy_ip";
const CompressionType HttpTransactionData::default_response_content_encoding = CompressionType::NO_COMPRESSION;
Maybe<uint16_t>
deserializeUintParam(const Buffer &data, uint &cur_pos)
{
// Int value is encoded in binary form
auto value = data.getTypePtr<uint16_t>(cur_pos);
if (!value.ok()) {
return genError("Failed to get Uint param " + value.getErr());
}
cur_pos += sizeof(uint16_t);
dbgTrace(D_NGINX_ATTACHMENT) << "Successfully parsed the number parameter. Value: " << *(value.unpack());
return *(value.unpack());
}
Maybe<string>
deserializeStrParam(const Buffer &data, uint &cur_pos)
{
//String is encoded by 16-bit uint representing length followed by const c-type string data bytes
Maybe<uint16_t> str_size = deserializeUintParam(data, cur_pos);
if (!str_size.ok()) return genError("Could Not parse string size value: " + str_size.getErr());
dbgTrace(D_NGINX_ATTACHMENT)
<< "Deserializing string parameter. Current position: "
<< cur_pos
<< ", String size: "
<< *str_size;
string res = "";
if (*str_size > 0) {
auto value = data.getPtr(cur_pos, *str_size);
if (!value.ok()) {
return genError("Failed to get String param " + value.getErr());
}
const u_char *ptr = value.unpack();
res = string(reinterpret_cast<const char *>(ptr), *str_size);
}
dbgTrace(D_NGINX_ATTACHMENT)
<< "Successfully parsed string parameter. Result: "
<< res
<< ", Length: "
<< to_string(*str_size);
cur_pos += *str_size;
return move(res);
}
Maybe<IPAddr>
deserializeIpAddrParam(const Buffer &data, uint &cur_pos)
{
Maybe<string> str_value = deserializeStrParam(data, cur_pos);
if (!str_value.ok()) return str_value.passErr();
Maybe<IPAddr> ip = IPAddr::createIPAddr(str_value.unpackMove());
if (!ip.ok()) return genError("Could not parse IP Address: " + ip.getErr());
return move(ip.unpackMove());
}
Maybe<HttpTransactionData>
HttpTransactionData::createTransactionData(const Buffer &transaction_raw_data)
{
// Deserialize TransactionData from binary blob sent from attachment
uint cur_pos = 0;
dbgTrace(D_NGINX_ATTACHMENT)
<< "Parsing buffer "
<< dumpHex(transaction_raw_data)
<< " of size "
<< transaction_raw_data.size();
Maybe<string> http_protocol = deserializeStrParam(transaction_raw_data, cur_pos);
if (!http_protocol.ok()) {
return genError("Could not deserialize HTTP protocol: " + http_protocol.getErr());
} else {
dbgTrace(D_NGINX_ATTACHMENT) << "Successfully deserialized HTTP protocol: " << http_protocol.unpack();
}
Maybe<string> http_method = deserializeStrParam(transaction_raw_data, cur_pos);
if (!http_method.ok()) {
return genError("Could not deserialize HTTP method: " + http_method.getErr());
} else {
dbgTrace(D_NGINX_ATTACHMENT) << "Successfully deserialized HTTP method: " << http_method.unpack();
}
Maybe<string> host_name = deserializeStrParam(transaction_raw_data, cur_pos);
if (!host_name.ok()) {
return genError("Could not deserialize host name: " + host_name.getErr());
} else {
dbgTrace(D_NGINX_ATTACHMENT) << "Successfully deserialized host name: " << host_name.unpack();
}
Maybe<IPAddr> listening_addr = deserializeIpAddrParam(transaction_raw_data, cur_pos);
if (!listening_addr.ok()) {
return genError("Could not deserialize listening address: " + listening_addr.getErr());
} else {
dbgTrace(D_NGINX_ATTACHMENT) << "Successfully deserialized listening address: " << listening_addr.unpack();
}
Maybe<uint32_t> listening_port = deserializeUintParam(transaction_raw_data, cur_pos);
if (!listening_port.ok()) {
return genError("Could not deserialize listening port: " + listening_port.getErr());
} else {
dbgTrace(D_NGINX_ATTACHMENT) << "Successfully deserialized listening port: " << listening_port.unpack();
}
Maybe<string> uri = deserializeStrParam(transaction_raw_data, cur_pos);
if (!uri.ok()) {
return genError("Could not deserialize URI: " + uri.getErr());
} else {
dbgTrace(D_NGINX_ATTACHMENT) << "Successfully deserialized URI: " << uri.unpack();
}
Maybe<IPAddr> client_addr = deserializeIpAddrParam(transaction_raw_data, cur_pos);
if (!client_addr.ok()) {
return genError("Could not deserialize client address: " + client_addr.getErr());
} else {
dbgTrace(D_NGINX_ATTACHMENT) << "Successfully deserialized client address: " << client_addr.unpack();
}
Maybe<uint32_t> client_port = deserializeUintParam(transaction_raw_data, cur_pos);
if (!client_port.ok()) {
return genError("Could not deserialize client port: " + client_port.getErr());
} else {
dbgTrace(D_NGINX_ATTACHMENT) << "Successfully deserialized client port: " << client_port.unpack();
}
// Fail if after parsing exact number of items, we didn't exactly consume whole buffer
if (cur_pos != transaction_raw_data.size()) {
dbgWarning(D_NGINX_ATTACHMENT) << "Nothing to deserialize, but raw data still remain";
return genError("Finished deserialization and raw data still exist - Probably corrupted buffer.");
}
HttpTransactionData transaction(
http_protocol.unpackMove(),
http_method.unpackMove(),
host_name.unpackMove(),
listening_addr.unpackMove(),
listening_port.unpackMove(),
uri.unpackMove(),
client_addr.unpackMove(),
client_port.unpackMove()
);
return move(transaction);
}
HttpTransactionData::HttpTransactionData (
string _http_proto,
string _method,
string _host_name,
IPAddr _listening_ip,
uint16_t _listening_port,
string _uri,
IPAddr _client_ip,
uint16_t _client_port
)
:
http_proto(move(_http_proto)),
method(move(_method)),
host_name(move(_host_name)),
listening_ip(move(_listening_ip)),
listening_port(move(_listening_port)),
uri(move(_uri)),
client_ip(move(_client_ip)),
client_port(move(_client_port)),
is_request(true),
response_content_encoding(default_response_content_encoding)
{
}
HttpTransactionData::HttpTransactionData()
:
HttpTransactionData::HttpTransactionData(
"",
"GET",
"",
IPAddr(),
-1,
"",
IPAddr(),
-1
)
{}
void
HttpTransactionData::print(ostream &out_stream) const
{
out_stream << http_proto << " " << method << endl;
out_stream << "From: " << client_ip << ":" << client_port << endl;
out_stream << "To: "
<< host_name
<< uri
<< " (listening on "
<< listening_ip
<< ":"
<< listening_port
<< ")"
<< endl;
}
SASAL_END

View File

@@ -0,0 +1,7 @@
include_directories(${CMAKE_SOURCE_DIR}/components/include)
add_unit_test(
http_transaction_data_ut
"http_transaction_data_ut.cc"
"http_transaction_data;http_transaction_data;connkey;${RT_LIBRARY}"
)

View File

@@ -0,0 +1,127 @@
#include "http_transaction_data.h"
#include <string>
#include <sstream>
#include "cptest.h"
using namespace std;
using namespace testing;
Buffer
encodeInt16(uint16_t val)
{
vector<u_char> raw_data(reinterpret_cast<u_char*>(&val), reinterpret_cast<u_char*>(&val) + sizeof(uint16_t));
return move(Buffer(raw_data));
}
class HttpTransactionTest : public Test
{
public:
Buffer
createValidBuf()
{
Buffer protocol_length = Buffer(encodeInt16(strlen("HTTP/1.1")));
return
protocol_length +
Buffer("HTTP/1.1") +
encodeInt16(3) +
Buffer("GET") +
encodeInt16(9) +
Buffer("localhost") +
encodeInt16(7) +
Buffer("0.0.0.0") +
encodeInt16(443) +
encodeInt16(10) +
Buffer("/user-app/") +
encodeInt16(9) +
Buffer("127.0.0.1") +
encodeInt16(47423);
}
Buffer
createBadVerBuf()
{
Buffer protocol_length = Buffer(encodeInt16(strlen("HTTP/1.1")));
return
protocol_length +
Buffer("HTTP/1");
}
Buffer
createBadAddressBuf()
{
Buffer protocol_length = Buffer(encodeInt16(strlen("HTTP/1.1")));
return
protocol_length +
Buffer("HTTP/1.1") +
encodeInt16(3) +
Buffer("GET") +
encodeInt16(9) +
Buffer("localhost") +
encodeInt16(14) +
Buffer("this.is.not.IP") +
encodeInt16(443) +
encodeInt16(10) +
Buffer("/user-app/") +
encodeInt16(9) +
Buffer("127.0.0.1") +
encodeInt16(47423);
}
};
TEST_F(HttpTransactionTest, TestEmptyTransactionData)
{
HttpTransactionData data;
stringstream data_stream;
data.print(data_stream);
string data_string(
" GET\nFrom: Uninitialized IP address:65535\nTo: (listening on Uninitialized IP address:65535)\n"
);
EXPECT_EQ(data_stream.str(), data_string);
}
TEST_F(HttpTransactionTest, TestTransactionDataFromBuf)
{
HttpTransactionData data = HttpTransactionData::createTransactionData(createValidBuf()).unpack();
stringstream data_stream;
data.print(data_stream);
string data_string(
"HTTP/1.1 GET\nFrom: 127.0.0.1:47423\nTo: localhost/user-app/ (listening on 0.0.0.0:443)\n"
);
EXPECT_EQ(data_stream.str(), data_string);
EXPECT_EQ(data.getSourceIP(), IPAddr::createIPAddr("127.0.0.1").unpack());
EXPECT_EQ(data.getSourcePort(), 47423);
EXPECT_EQ(data.getListeningIP(), IPAddr::createIPAddr("0.0.0.0").unpack());
EXPECT_EQ(data.getListeningPort(), 443);
EXPECT_EQ(data.getDestinationHost(), "localhost");
EXPECT_EQ(data.getHttpProtocol(), "HTTP/1.1");
EXPECT_EQ(data.getURI(), "/user-app/");
EXPECT_EQ(data.getHttpMethod(), "GET");
}
TEST_F(HttpTransactionTest, TestTransactionDataBadVer)
{
auto data = HttpTransactionData::createTransactionData(createBadVerBuf());
ASSERT_FALSE(data.ok());
EXPECT_EQ(
data.getErr(),
"Could not deserialize HTTP protocol: "
"Failed to get String param Cannot get internal pointer beyond the buffer limits"
);
}
TEST_F(HttpTransactionTest, TestTransactionDataBadAddress)
{
auto data = HttpTransactionData::createTransactionData(createBadAddressBuf());
ASSERT_FALSE(data.ok());
EXPECT_EQ(
data.getErr(),
"Could not deserialize listening address: "
"Could not parse IP Address: String 'this.is.not.IP' is not a valid IPv4/IPv6 address"
);
}

71
components/include/WaapEnums.h Executable file
View File

@@ -0,0 +1,71 @@
// 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 __WAAP_ENUMS_H__
#define __WAAP_ENUMS_H__
#include <cstddef>
#include <functional>
#define NO_THREAT_FINAL_SCORE 0.0
#define INFO_THREAT_THRESHOLD 1.0
#define LOW_THREAT_THRESHOLD 3.0
#define MED_THREAT_THRESHOLD 6.0
#define MAX_FINAL_SCORE 10.0
#define ATTACK_IN_PARAM "attack_in_param"
enum ThreatLevel {
NO_THREAT = 0,
THREAT_INFO,
LOW_THREAT,
MEDIUM_THREAT,
HIGH_THREAT
};
enum BlockType {
NOT_BLOCKING,
FORCE_EXCEPTION,
FORCE_BLOCK,
API_BLOCK,
BOT_BLOCK,
WAF_BLOCK,
CSRF_BLOCK,
LIMIT_BLOCK
};
enum ParamType {
UNKNOWN_PARAM_TYPE,
HTML_PARAM_TYPE,
URL_PARAM_TYPE,
FREE_TEXT_PARAM_TYPE,
PIPE_PARAM_TYPE,
LONG_RANDOM_TEXT_PARAM_TYPE,
BASE64_PARAM_TYPE,
ADMINISTRATOR_CONFIG_PARAM_TYPE,
FILE_PATH_PARAM_TYPE,
SEMICOLON_DELIMITED_PARAM_TYPE,
ASTERISK_DELIMITED_PARAM_TYPE,
COMMA_DELIMITED_PARAM_TYPE,
AMPERSAND_DELIMITED_PARAM_TYPE,
BINARY_PARAM_TYPE,
PARAM_TYPE_COUNT
};
namespace std {
template<>
struct hash<ParamType>
{
std::size_t operator()(const ParamType& type) const noexcept { return (size_t)type; }
};
}
#endif

View File

@@ -0,0 +1,47 @@
// 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 __ATTACHMENT_REGISTRATOR_H__
#define __ATTACHMENT_REGISTRATOR_H__
#include "singleton.h"
#include "i_mainloop.h"
#include "i_shell_cmd.h"
#include "i_socket_is.h"
#include "attachment_types.h"
#include "component.h"
#define default_keep_alive_path "/etc/cp/attachmentRegistrator/expiration-socket"
class AttachmentRegistrator
:
public Component,
Singleton::Consume<I_MainLoop>,
Singleton::Consume<I_ShellCmd>,
Singleton::Consume<I_Socket>
{
public:
AttachmentRegistrator();
~AttachmentRegistrator();
void preload();
void init();
void fini();
private:
class Impl;
std::unique_ptr<Impl> pimpl;
};
#endif // __ATTACHMENT_REGISTRATOR_H__

48
components/include/byteorder.h Executable file
View File

@@ -0,0 +1,48 @@
#ifndef __BYTEORDER_H__
#define __BYTEORDER_H__
// Byte Order (Net-to-Host, Host-to-Net) operations
//
// C provides htons, ntohs, htonl, ntohl, but they're not "constexpr" so are unusable in case labels.
// C++ proposal N3620 adds some function, but it's not accepted (yet?). It uses templates which are,
// IMO, a bit complicated so I chose not to adapt it.
static inline constexpr uint16_t
constHTONS(uint16_t h_ord)
{
#if __BYTE_ORDER == __LITTLE_ENDIAN
return ((h_ord>>8) & 0xff) |
((h_ord&0xff) << 8);
#elif __BYTE_ORDER == __BIG_ENDIAN
return h_ord;
#else
#error unknown byte order
#endif // __BYTE_ORDER
}
static inline constexpr uint16_t
constNTOHS(uint16_t n_ord)
{
return constHTONS(n_ord); // Same thing
}
static inline constexpr uint32_t
constHTONL(uint32_t h_ord)
{
#if __BYTE_ORDER == __LITTLE_ENDIAN
return ((constHTONS(h_ord>>16)) & 0xffff) |
((constHTONS(h_ord&0xffff)) << 16);
#elif __BYTE_ORDER == __BIG_ENDIAN
return h_ord;
#else
#error unknown byte order
#endif // __BYTE_ORDER
}
static inline constexpr uint32_t
constNTOHL(uint32_t n_ord)
{
return constHTONL(n_ord); // Same thing
}
#endif // __BYTEORDER_H__

View 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 __DETAILS_RESOLVER_H__
#define __DETAILS_RESOLVER_H__
#include <memory>
#include <string>
#include "i_orchestration_tools.h"
#include "i_details_resolver.h"
#include "i_shell_cmd.h"
#include "singleton.h"
#include "component.h"
class DetailsResolver
:
public Component,
Singleton::Provide<I_DetailsResolver>,
Singleton::Consume<I_OrchestrationTools>
{
public:
DetailsResolver();
~DetailsResolver();
void preload() override;
private:
class Impl;
std::unique_ptr<Impl> pimpl;
};
#endif // __DETAILS_RESOLVER_H__

50
components/include/downloader.h Executable file
View File

@@ -0,0 +1,50 @@
// 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 __DOWNLOADER_H__
#define __DOWNLOADER_H__
#include "i_downloader.h"
#include "i_orchestration_tools.h"
#include "i_update_communication.h"
#include "i_encryptor.h"
#include "url_parser.h"
#include "i_agent_details.h"
#include "i_mainloop.h"
#include "singleton.h"
#include "component.h"
class Downloader
:
public Component,
Singleton::Provide<I_Downloader>,
Singleton::Consume<I_AgentDetails>,
Singleton::Consume<I_Encryptor>,
Singleton::Consume<I_MainLoop>,
Singleton::Consume<I_OrchestrationTools>,
Singleton::Consume<I_UpdateCommunication>
{
public:
Downloader();
~Downloader();
void preload() override;
void init() override;
private:
class Impl;
std::unique_ptr<Impl> pimpl;
};
#endif // __DOWNLOADER_H__

View 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 __EXTERNAL_SDK_SERVER_H__
#define __EXTERNAL_SDK_SERVER_H__
#include <memory>
#include "i_external_sdk_server.h"
#include "i_rest_api.h"
#include "component.h"
class ExternalSdkServer
:
public Component,
Singleton::Provide<I_ExternalSdkServer>,
Singleton::Consume<I_RestApi>
{
public:
ExternalSdkServer();
~ExternalSdkServer();
void init();
void fini();
void preload();
private:
class Impl;
std::unique_ptr<Impl> pimpl;
};
#endif // __EXTERNAL_SDK_SERVER_H__

View 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 __ASSET_H__
#define __ASSET_H__
#include <unordered_map>
#include "i_environment.h"
class Asset
{
public:
const std::map<Context::MetaDataType, std::string> & getAttrs() const { return attr; }
void setAttr(Context::MetaDataType type, const std::string &attr_val) { attr[type] = attr_val; }
private:
std::map<Context::MetaDataType, std::string> attr;
};
#endif // __ASSET_H__

View 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 __ASSETS_CONFIG_H__
#define __ASSETS_CONFIG_H__
#include <vector>
#include <string>
#include <arpa/inet.h>
#include "generic_rulebase_context.h"
#include "c_common/ip_common.h"
#include "connkey.h"
class RuleAsset
{
public:
class AssetUrl
{
public:
void
load(cereal::JSONInputArchive &archive_in);
const std::string & getProtocol() const { return protocol; }
const std::string & getIp() const { return ip; }
const std::string & getPort() const { return port; }
uint8_t getParsedProtocol() const { return parsed_proto; }
const IpAddress & getParsedIp() const { return parsed_ip; }
uint16_t getParsedPort() const { return parsed_port; }
bool isAnyIp() const { return is_any_ip; }
bool isAnyPort() const { return is_any_port; }
bool isAnyProto() const { return is_any_proto; }
private:
static IpAddress ConvertToIpAddress(const IPAddr &addr);
std::string protocol;
std::string ip;
std::string port;
IpAddress parsed_ip;
uint16_t parsed_port;
uint8_t parsed_proto;
bool is_any_ip;
bool is_any_port;
bool is_any_proto;
};
void load(cereal::JSONInputArchive &archive_in);
const GenericConfigId & getId() const { return asset_id; }
const std::string & getName() const { return asset_name; }
const std::vector<AssetUrl> & getUrls() const { return asset_urls; }
private:
GenericConfigId asset_id;
std::string asset_name;
std::vector<AssetUrl> asset_urls;
};
class Assets
{
public:
static void preload();
void load(cereal::JSONInputArchive &archive_in)
{
try {
cereal::load(archive_in, assets);
}catch (const cereal::Exception &) {
}
}
static const Assets empty_assets_config;
const std::vector<RuleAsset> & getAssets() const { return assets; }
private:
std::vector<RuleAsset> assets;
};
#endif //__ASSETS_CONFIG_H__

View File

@@ -0,0 +1,36 @@
// 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 __ASSET_EVAL_H__
#define __ASSET_EVAL_H__
#include "environment/evaluator_templates.h"
#include "i_environment.h"
#include "singleton.h"
class AssetMatcher : public EnvironmentEvaluator<bool>, Singleton::Consume<I_Environment>
{
public:
AssetMatcher(const std::vector<std::string> &params);
static std::string getName() { return "assetId"; }
Maybe<bool, Context::Error> evalVariable() const override;
static std::string ctx_key;
private:
std::string asset_id;
};
#endif // __ASSET_EVAL_H__

View File

@@ -0,0 +1,127 @@
// 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 __CONNECTION_EVAL_H__
#define __CONNECTION_EVAL_H__
#include "environment/evaluator_templates.h"
#include "i_environment.h"
#include "singleton.h"
#include "connkey.h"
class IpAddressMatcher : public EnvironmentEvaluator<bool>, Singleton::Consume<I_Environment>
{
public:
IpAddressMatcher(const std::vector<std::string> &params);
static std::string getName() { return "ipAddress"; }
Maybe<bool, Context::Error> evalVariable() const override;
static std::string ctx_key;
private:
std::vector<CustomRange<IPAddr>> values;
};
class SourceIpMatcher : public EnvironmentEvaluator<bool>, Singleton::Consume<I_Environment>
{
public:
SourceIpMatcher(const std::vector<std::string> &params);
static std::string getName() { return "sourceIP"; }
Maybe<bool, Context::Error> evalVariable() const override;
static std::string ctx_key;
private:
std::vector<CustomRange<IPAddr>> values;
};
class DestinationIpMatcher : public EnvironmentEvaluator<bool>, Singleton::Consume<I_Environment>
{
public:
DestinationIpMatcher(const std::vector<std::string> &params);
static std::string getName() { return "destinationIP"; }
Maybe<bool, Context::Error> evalVariable() const override;
static std::string ctx_key;
private:
std::vector<CustomRange<IPAddr>> values;
};
class SourcePortMatcher : public EnvironmentEvaluator<bool>, Singleton::Consume<I_Environment>
{
public:
SourcePortMatcher(const std::vector<std::string> &params);
static std::string getName() { return "sourcePort"; }
Maybe<bool, Context::Error> evalVariable() const override;
static std::string ctx_key;
private:
std::vector<CustomRange<PortNumber>> values;
};
class ListeningPortMatcher : public EnvironmentEvaluator<bool>, Singleton::Consume<I_Environment>
{
public:
ListeningPortMatcher(const std::vector<std::string> &params);
static std::string getName() { return "listeningPort"; }
Maybe<bool, Context::Error> evalVariable() const override;
static std::string ctx_key;
private:
std::vector<CustomRange<PortNumber>> values;
};
class IpProtocolMatcher : public EnvironmentEvaluator<bool>, Singleton::Consume<I_Environment>
{
public:
IpProtocolMatcher(const std::vector<std::string> &params);
static std::string getName() { return "ipProtocol"; }
Maybe<bool, Context::Error> evalVariable() const override;
static std::string ctx_key;
private:
std::vector<CustomRange<IPProto>> values;
};
class UrlMatcher : public EnvironmentEvaluator<bool>, Singleton::Consume<I_Environment>
{
public:
UrlMatcher(const std::vector<std::string> &params);
static std::string getName() { return "url"; }
Maybe<bool, Context::Error> evalVariable() const override;
static std::string ctx_key;
private:
std::vector<std::string> values;
};
#endif // __CONNECTION_EVAL_H__

View File

@@ -0,0 +1,74 @@
// 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_TRANSACTION_DATA_EVAL_H__
#define __HTTP_TRANSACTION_DATA_EVAL_H__
#include "environment/evaluator_templates.h"
#include "i_environment.h"
#include "singleton.h"
#include "connkey.h"
class EqualHost : public EnvironmentEvaluator<bool>, Singleton::Consume<I_Environment>
{
public:
EqualHost(const std::vector<std::string> &params);
static std::string getName() { return "EqualHost"; }
Maybe<bool, Context::Error> evalVariable() const override;
private:
std::string host;
};
class EqualListeningIP : public EnvironmentEvaluator<bool>, Singleton::Consume<I_Environment>
{
public:
EqualListeningIP(const std::vector<std::string> &params);
static std::string getName() { return "EqualListeningIP"; }
Maybe<bool, Context::Error> evalVariable() const override;
private:
IPAddr listening_ip;
};
class EqualListeningPort : public EnvironmentEvaluator<bool>, Singleton::Consume<I_Environment>
{
public:
EqualListeningPort(const std::vector<std::string> &params);
static std::string getName() { return "EqualListeningPort"; }
Maybe<bool, Context::Error> evalVariable() const override;
private:
PortNumber listening_port;
};
class BeginWithUri : public EnvironmentEvaluator<bool>, Singleton::Consume<I_Environment>
{
public:
BeginWithUri(const std::vector<std::string> &params);
static std::string getName() { return "BeginWithUri"; }
Maybe<bool, Context::Error> evalVariable() const override;
private:
std::string uri_prefix;
};
#endif // __HTTP_TRANSACTION_DATA_EVAL_H__

View File

@@ -0,0 +1,36 @@
// 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 __PARAMETER_EVAL_H__
#define __PARAMETER_EVAL_H__
#include "environment/evaluator_templates.h"
#include "i_environment.h"
#include "singleton.h"
class ParameterMatcher : public EnvironmentEvaluator<bool>, Singleton::Consume<I_Environment>
{
public:
ParameterMatcher(const std::vector<std::string> &params);
static std::string getName() { return "parameterId"; }
Maybe<bool, Context::Error> evalVariable() const override;
static std::string ctx_key;
private:
std::string parameter_id;
};
#endif // __PARAMETER_EVAL_H__

View File

@@ -0,0 +1,36 @@
// 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 __PRACTICE_EVAL_H__
#define __PRACTICE_EVAL_H__
#include "environment/evaluator_templates.h"
#include "i_environment.h"
#include "singleton.h"
class PracticeMatcher : public EnvironmentEvaluator<bool>, Singleton::Consume<I_Environment>
{
public:
PracticeMatcher(const std::vector<std::string> &params);
static std::string getName() { return "practiceId"; }
Maybe<bool, Context::Error> evalVariable() const override;
static std::string ctx_key;
private:
std::string practice_id;
};
#endif // __PRACTICE_EVAL_H__

View 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 __QUERY_EVAL_H__
#define __QUERY_EVAL_H__
#include "environment/evaluator_templates.h"
#include "i_environment.h"
#include "i_generic_rulebase.h"
#include "singleton.h"
class QueryMatcher
:
public EnvironmentEvaluator<bool>,
Singleton::Consume<I_Environment>,
Singleton::Consume<I_GenericRulebase>
{
public:
QueryMatcher(const std::vector<std::string> &query_params);
static std::string getName() { return "matchQuery"; }
Maybe<bool, Context::Error> evalVariable() const override;
private:
static const std::string contextKeyToString(Context::MetaDataType type);
std::string key;
std::unordered_set<std::string> values;
bool is_any = false;
};
#endif // __QUERY_EVAL_H__

View File

@@ -0,0 +1,36 @@
// 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 __TRIGGER_EVAL_H__
#define __TRIGGER_EVAL_H__
#include "environment/evaluator_templates.h"
#include "i_environment.h"
#include "singleton.h"
class TriggerMatcher : public EnvironmentEvaluator<bool>, Singleton::Consume<I_Environment>
{
public:
TriggerMatcher(const std::vector<std::string> &params);
static std::string getName() { return "triggerId"; }
Maybe<bool, Context::Error> evalVariable() const override;
static std::string ctx_key;
private:
std::string trigger_id;
};
#endif // __TRIGGER_EVAL_H__

View File

@@ -0,0 +1,36 @@
// 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 __ZONE_EVAL_H__
#define __ZONE_EVAL_H__
#include "environment/evaluator_templates.h"
#include "i_environment.h"
#include "singleton.h"
class ZoneMatcher : public EnvironmentEvaluator<bool>, Singleton::Consume<I_Environment>
{
public:
ZoneMatcher(const std::vector<std::string> &zones);
static std::string getName() { return "zoneId"; }
Maybe<bool, Context::Error> evalVariable() const override;
static std::string ctx_key;
private:
std::string zone_id;
};
#endif // __ZONE_EVAL_H__

View File

@@ -0,0 +1,44 @@
// 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 __GENERIC_RULEBASE_H__
#define __GENERIC_RULEBASE_H__
#include <memory>
#include "i_generic_rulebase.h"
#include "i_intelligence_is_v2.h"
#include "singleton.h"
#include "component.h"
class GenericRulebase
:
public Component,
Singleton::Provide<I_GenericRulebase>,
Singleton::Consume<I_Intelligence_IS_V2>
{
public:
GenericRulebase();
~GenericRulebase();
void preload();
void init();
void fini();
private:
class Impl;
std::unique_ptr<Impl> pimpl;
};
#endif // __GENERIC_RULEBASE_H__

View 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 __GENERIC_RULEBASE_CONTEXT_H__
#define __GENERIC_RULEBASE_CONTEXT_H__
#include "rulebase_config.h"
#include "context.h"
#include "config.h"
enum class RuleRegistrationState {REGISTERED, UNREGISTERED, UNINITIALIZED};
class GenericRulebaseContext
{
public:
GenericRulebaseContext() : ctx(), registration_state(RuleRegistrationState::UNINITIALIZED) {}
void activate(const BasicRuleConfig &rule);
void activate();
void deactivate() { if (registration_state == RuleRegistrationState::REGISTERED) ctx.deactivate(); }
private:
Context ctx;
RuleRegistrationState registration_state;
};
#endif //__GENERIC_RULEBASE_CONTEXT_H__

View 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 __GENERIC_RULEBASE_UTILS_H__
#define __GENERIC_RULEBASE_UTILS_H__
#include <string>
#include "debug.h"
#include "cereal/archives/json.hpp"
USE_DEBUG_FLAG(D_RULEBASE_CONFIG);
template <typename T>
void
parseJSONKey(const std::string &key_name, T &value, cereal::JSONInputArchive &archive_in)
{
try {
archive_in(cereal::make_nvp(key_name, value));
} catch (const cereal::Exception &e) {
dbgDebug(D_RULEBASE_CONFIG)
<< "Could not parse the required key. Key: "
<< key_name
<< ", Error: "
<< e.what();
}
}
#endif //__GENERIC_RULEBASE_UTILS_H__

View File

@@ -0,0 +1,93 @@
// 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 __MATCH_QUERY_H__
#define __MATCH_QUERY_H__
#include <vector>
#include <string>
#include <set>
#include <map>
#include <arpa/inet.h>
#include "cereal/types/string.hpp"
#include "cereal/types/vector.hpp"
#include "cereal/archives/json.hpp"
#include <boost/regex.hpp>
#include "c_common/ip_common.h"
class MatchQuery
{
public:
enum class MatchType { Condition, Operator };
enum class Operators { And, Or, None };
enum class Conditions { Equals, NotEquals, In, NotIn, Exist, None };
enum class StaticKeys
{
IpAddress,
SrcIpAddress,
DstIpAddress,
SrcPort,
ListeningPort,
IpProtocol,
Domain,
NotStatic
};
void load(cereal::JSONInputArchive &archive_in);
MatchType getType() const { return type; }
Operators getOperatorType() const { return operator_type; }
Conditions getConditionType() const { return condition_type; }
const std::string & getKey() const { return key; }
const std::set<std::string> & getValue() const { return value; }
const std::vector<IPRange> & getIpAddrValue() const { return ip_addr_value; }
const std::vector<PortsRange> & getPortValue() const { return port_value; }
const std::vector<IpProtoRange> & getProtoValue() const { return ip_proto_value; }
const std::vector<MatchQuery> & getItems() const { return items; }
std::string getFirstValue() const { return first_value; }
bool matchAttributes(const std::unordered_map<std::string, std::set<std::string>> &key_value_pairs) const;
bool matchException(const std::string &behaviorKey, const std::string &behaviorValue) const;
bool isKeyTypeIp() const;
bool isKeyTypePort() const;
bool isKeyTypeProtocol() const;
bool isKeyTypeDomain() const;
bool isKeyTypeSpecificLabel() const;
bool isKeyTypeStatic() const;
std::set<std::string> getAllKeys() const;
private:
StaticKeys getKeyByName(const std::string &key_type_name);
bool matchAttributes(const std::set<std::string> &values) const;
bool matchAttributesRegEx(const std::set<std::string> &values) const;
bool matchAttributesString(const std::set<std::string> &values) const;
bool isRegEx() const;
MatchType type;
Operators operator_type;
Conditions condition_type;
std::string key;
StaticKeys key_type;
bool is_specific_label;
std::string first_value;
std::set<std::string> value;
std::set<boost::regex> regex_values;
std::vector<IPRange> ip_addr_value;
std::vector<PortsRange> port_value;
std::vector<IpProtoRange> ip_proto_value;
std::vector<MatchQuery> items;
};
#endif // __MATCH_QUERY_H__

View File

@@ -0,0 +1,221 @@
// 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 __PARAMETERS_CONFIG_H__
#define __PARAMETERS_CONFIG_H__
#include <string>
#include <vector>
#include <set>
#include <unordered_map>
#include "cereal/types/string.hpp"
#include "cereal/types/vector.hpp"
#include "cereal/archives/json.hpp"
#include "generic_rulebase/generic_rulebase_utils.h"
#include "match_query.h"
#include "maybe_res.h"
#include "config.h"
enum class BehaviorKey
{
ACTION,
LOG,
SOURCE_IDENTIFIER,
HTTP_SOURCE_ID,
HTTPS_SOURCE_ID
};
enum class BehaviorValue
{
REJECT,
ACCEPT,
IGNORE,
DROP,
X_FORWARDED_FOR,
COOKIE_AOUTH2_PROXY,
COOKIE_JSESSIONID
};
static const std::unordered_map<std::string, BehaviorKey> string_to_behavior_key = {
{ "action", BehaviorKey::ACTION },
{ "log", BehaviorKey::LOG },
{ "sourceIdentifier", BehaviorKey::SOURCE_IDENTIFIER },
{ "httpSourceId", BehaviorKey::HTTP_SOURCE_ID },
{ "httpsSourceId", BehaviorKey::HTTPS_SOURCE_ID }
};
static const std::unordered_map<std::string, BehaviorValue> string_to_behavior_val = {
{ "Cookie:_oauth2_proxy", BehaviorValue::COOKIE_AOUTH2_PROXY },
{ "Cookie:JSESSIONID", BehaviorValue::COOKIE_JSESSIONID },
{ "X-Forwarded-For", BehaviorValue::X_FORWARDED_FOR },
{ "reject", BehaviorValue::REJECT },
{ "accept", BehaviorValue::ACCEPT },
{ "ignore", BehaviorValue::IGNORE },
{ "drop", BehaviorValue::DROP }
};
class ParameterOverrides
{
public:
class ParsedBehavior
{
public:
void
serialize(cereal::JSONInputArchive &archive_in)
{
parseJSONKey<std::string>("log", log, archive_in);
}
const std::string & getParsedBehaviorLog() const { return log; }
private:
std::string log;
};
void load(cereal::JSONInputArchive &archive_in);
const std::vector<ParsedBehavior> & getParsedBehaviors() const { return parsed_behaviors; }
private:
std::vector<ParsedBehavior> parsed_behaviors;
};
class ParameterTrustedSources
{
public:
class SourcesIdentifier
{
public:
SourcesIdentifier() = default;
void
serialize(cereal::JSONInputArchive &archive_in)
{
parseJSONKey<std::string>("sourceIdentifier", source_identifier, archive_in);
parseJSONKey<std::string>("value", value, archive_in);
}
const std::string & getSourceIdentifier() const {return source_identifier; }
const std::string & getValue() const {return value; }
private:
std::string source_identifier;
std::string value;
};
void load(cereal::JSONInputArchive &archive_in);
uint getNumOfSources() const { return num_of_sources; }
const std::vector<SourcesIdentifier> & getSourcesIdentifiers() const { return sources_identidiers; }
private:
uint num_of_sources;
std::vector<SourcesIdentifier> sources_identidiers;
};
class ParameterBehavior
{
public:
ParameterBehavior() = default;
ParameterBehavior(BehaviorKey &_key, BehaviorValue &_value) : key(_key), value(_value) {}
ParameterBehavior(BehaviorKey &&_key, BehaviorValue &&_value)
:
key(std::move(_key)),
value(std::move(_value))
{}
void load(cereal::JSONInputArchive &archive_in);
const BehaviorValue & getValue() const { return value; }
const BehaviorKey & getKey() const { return key; }
const std::string & getId() const { return id; }
bool
operator<(const ParameterBehavior &other) const {
return (key < other.key) || (key == other.key && value < other.value);
}
bool operator==(const ParameterBehavior &other) const { return key == other.key && value == other.value; }
private:
std::string id;
BehaviorKey key;
BehaviorValue value;
};
class ParameterAntiBot
{
public:
void load(cereal::JSONInputArchive &archive_in);
std::vector<std::string> & getInjected() { return injected; }
std::vector<std::string> & getValidated() { return validated; }
private:
std::vector<std::string> injected;
std::vector<std::string> validated;
};
class ParameterOAS
{
public:
void load(cereal::JSONInputArchive &archive_in);
const std::string & getValue() const { return value; }
private:
std::string value;
};
class ParameterException
{
public:
static void
preload()
{
registerExpectedConfiguration<ParameterException>("rulebase", "exception");
registerConfigLoadCb([](){ is_geo_location_exception_exists = is_geo_location_exception_being_loaded; });
registerConfigPrepareCb([](){ is_geo_location_exception_being_loaded = false; });
}
void load(cereal::JSONInputArchive &archive_in);
std::set<ParameterBehavior>
getBehavior(const std::unordered_map<std::string, std::set<std::string>> &key_value_pairs) const;
static bool isGeoLocationExceptionExists() { return is_geo_location_exception_exists; }
private:
class MatchBehaviorPair
{
public:
void load(cereal::JSONInputArchive &archive_in);
MatchQuery match;
ParameterBehavior behavior;
};
std::vector<MatchBehaviorPair> match_queries;
MatchQuery match;
ParameterBehavior behavior;
static bool is_geo_location_exception_exists;
static bool is_geo_location_exception_being_loaded;
};
#endif //__PARAMETERS_CONFIG_H__

View File

@@ -0,0 +1,167 @@
// 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 __RULEBASE_CONFIG_H__
#define __RULEBASE_CONFIG_H__
#include <vector>
#include <string>
#include <set>
#include <unordered_map>
#include "generic_rulebase/generic_rulebase_utils.h"
#include "environment/evaluator_templates.h"
#include "cereal/types/string.hpp"
#include "cereal/types/vector.hpp"
#include "cereal/archives/json.hpp"
#include "i_environment.h"
#include "singleton.h"
#include "maybe_res.h"
#include "config.h"
using GenericConfigId = std::string;
class RulePractice
{
public:
RulePractice() = default;
RulePractice(GenericConfigId &_id, std::string &_name) : practice_id(_id), practice_name(_name) {};
void
serialize(cereal::JSONInputArchive &ar)
{
parseJSONKey<GenericConfigId>("practiceId", practice_id, ar);
parseJSONKey<std::string>("practiceName", practice_name, ar);
}
const GenericConfigId getId() const { return practice_id; }
const std::string getName() const { return practice_name; }
bool
operator==(const RulePractice &other) const
{
return practice_id == other.getId() && practice_name == other.getName();
}
private:
GenericConfigId practice_id;
std::string practice_name;
};
class RuleTrigger
{
public:
void
serialize(cereal::JSONInputArchive &ar)
{
parseJSONKey<GenericConfigId>("triggerId", trigger_id, ar);
parseJSONKey<std::string>("triggerType", trigger_type, ar);
parseJSONKey<std::string>("triggerName", trigger_name, ar);
}
const GenericConfigId getId() const { return trigger_id; }
const std::string getType() const { return trigger_type; }
const std::string getName() const { return trigger_name; }
private:
GenericConfigId trigger_id;
std::string trigger_type;
std::string trigger_name;
};
class RuleParameter
{
public:
void
serialize(cereal::JSONInputArchive &ar)
{
parseJSONKey<GenericConfigId>("parameterId", parameter_id, ar);
parseJSONKey<std::string>("parameterType", parameter_type, ar);
parseJSONKey<std::string>("parameterName", parameter_name, ar);
}
const GenericConfigId getId() const { return parameter_id; }
const std::string getType() const { return parameter_type; }
const std::string getName() const { return parameter_name; }
private:
GenericConfigId parameter_id;
std::string parameter_type;
std::string parameter_name;
};
class BasicRuleConfig
{
public:
static void
preload()
{
registerExpectedConfiguration<BasicRuleConfig>("rulebase", "rulesConfig");
registerExpectedSetting<std::vector<BasicRuleConfig>>("rulebase", "rulesConfig");
registerConfigLoadCb(BasicRuleConfig::updateCountMetric);
registerConfigPrepareCb([](){ BasicRuleConfig::assets_ids_aggregation.clear(); });
}
void load(cereal::JSONInputArchive &ar);
static void updateCountMetric();
bool isPracticeActive(const GenericConfigId &practice_id) const;
bool isTriggerActive(const GenericConfigId &trigger_id) const;
bool isParameterActive(const GenericConfigId &parameter_id) const;
uint8_t getPriority() const { return priority; }
const GenericConfigId & getRuleId() const { return rule_id; }
const std::string & getRuleName() const { return rule_name; }
const GenericConfigId & getAssetId() const { return asset_id; }
const std::string & getAssetName() const { return asset_name; }
const GenericConfigId & getZoneId() const { return zone_id; }
const std::string & getZoneName() const { return zone_name; }
const std::vector<RulePractice> & getPractices() const { return practices; }
const std::vector<RuleTrigger> & getTriggers() const { return triggers; }
const std::vector<RuleParameter> & getParameters() const { return parameters; }
private:
uint8_t priority = 0;
GenericConfigId rule_id = "";
std::string rule_name;
GenericConfigId asset_id;
std::string asset_name;
GenericConfigId zone_id;
std::string zone_name;
std::vector<RulePractice> practices;
std::vector<RuleTrigger> triggers;
std::vector<RuleParameter> parameters;
static std::set<std::string> assets_ids;
static std::set<std::string> assets_ids_aggregation;
};
#endif // __RULEBASE_CONFIG_H__

View File

@@ -0,0 +1,174 @@
// 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_CONFIG_H__
#define __TRIGGERS_CONFIG_H__
#include <vector>
#include <string>
#include "environment/evaluator_templates.h"
#include "cereal/types/string.hpp"
#include "cereal/types/vector.hpp"
#include "cereal/archives/json.hpp"
#include "i_environment.h"
#include "i_logging.h"
#include "singleton.h"
#include "maybe_res.h"
#include "config.h"
#include "log_generator.h"
#include "generic_rulebase_utils.h"
class WebTriggerConf
{
public:
WebTriggerConf();
WebTriggerConf(const std::string &title, const std::string &body, uint code);
static void
preload()
{
registerExpectedConfiguration<WebTriggerConf>("rulebase", "webUserResponse");
}
void load(cereal::JSONInputArchive &archive_in);
bool operator==(const WebTriggerConf &other) const;
uint getResponseCode() const { return response_code; }
const std::string & getResponseTitle() const { return response_title; }
const std::string & getResponseBody() const { return response_body; }
const std::string & getDetailsLevel() const { return details_level; }
const std::string & getRedirectURL() const { return redirect_url; }
bool getAddEventId() const { return add_event_id_to_header; }
static WebTriggerConf default_trigger_conf;
private:
std::string response_title;
std::string details_level;
std::string response_body;
std::string redirect_url;
uint response_code;
bool add_event_id_to_header = false;
};
class LogTriggerConf : Singleton::Consume<I_Logging>
{
public:
enum class SecurityType { AccessControl, ThreatPrevention, Compliance, COUNT };
enum class extendLoggingSeverity { None, High, Critical };
enum class WebLogFields {
webBody,
webHeaders,
webRequests,
webUrlPath,
webUrlQuery,
responseBody,
responseCode,
COUNT
};
LogTriggerConf() {}
LogTriggerConf(std::string trigger_name, bool log_detect, bool log_prevent);
static void
preload()
{
registerExpectedConfiguration<LogTriggerConf>("rulebase", "log");
}
template <typename ...Tags>
LogGen
operator()(
const std::string &title,
SecurityType security,
ReportIS::Severity severity,
ReportIS::Priority priority,
bool is_action_drop_or_prevent,
Tags ...tags) const
{
return LogGen(
title,
ReportIS::Level::LOG,
ReportIS::Audience::SECURITY,
severity,
priority,
std::forward<Tags>(tags)...,
getStreams(security, is_action_drop_or_prevent),
getEnrechments(security)
);
}
template <typename ...Tags>
LogGen
operator()(const std::string &title, SecurityType security, bool is_action_drop_or_prevent, Tags ...tags) const
{
return (*this)(
title,
security,
getSeverity(is_action_drop_or_prevent),
getPriority(is_action_drop_or_prevent),
is_action_drop_or_prevent,
std::forward<Tags>(tags)...
);
}
void load(cereal::JSONInputArchive &archive_in);
bool isWebLogFieldActive(WebLogFields log_field) const { return log_web_fields.isSet(log_field); }
bool isLogStreamActive(ReportIS::StreamType stream_type) const { return active_streams.isSet(stream_type); }
bool isPreventLogActive(SecurityType security_type) const { return should_log_on_prevent.isSet(security_type); }
bool isDetectLogActive(SecurityType security_type) const { return should_log_on_detect.isSet(security_type); }
bool isLogGeoLocationActive(SecurityType security_type) const { return log_geo_location.isSet(security_type); }
extendLoggingSeverity getExtendLoggingSeverity() const { return extend_logging_severity; }
const std::string & getVerbosity() const { return verbosity; }
const std::string & getName() const { return name; }
const std::string & getUrlForSyslog() const { return url_for_syslog; }
const std::string & getUrlForCef() const { return url_for_cef; }
private:
ReportIS::Severity getSeverity(bool is_action_drop_or_prevent) const;
ReportIS::Priority getPriority(bool is_action_drop_or_prevent) const;
Flags<ReportIS::StreamType> getStreams(SecurityType security_type, bool is_action_drop_or_prevent) const;
Flags<ReportIS::Enreachments> getEnrechments(SecurityType security_type) const;
std::string name;
std::string verbosity;
std::string url_for_syslog = "";
std::string url_for_cef = "";
Flags<ReportIS::StreamType> active_streams;
Flags<SecurityType> should_log_on_detect;
Flags<SecurityType> should_log_on_prevent;
Flags<SecurityType> log_geo_location;
Flags<WebLogFields> log_web_fields;
extendLoggingSeverity extend_logging_severity = extendLoggingSeverity::None;
bool should_format_output = false;
};
#endif //__TRIGGERS_CONFIG_H__

View File

@@ -0,0 +1,55 @@
// 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 __ZONE_H__
#define __ZONE_H__
#include <vector>
#include <string>
#include <set>
#include <arpa/inet.h>
#include "generic_rulebase_context.h"
#include "match_query.h"
#include "i_environment.h"
#include "i_intelligence_is_v2.h"
#include "asset.h"
class Zone : Singleton::Consume<I_Intelligence_IS_V2>, Singleton::Consume<I_Environment>
{
using AttrData = std::unordered_map<std::string, std::set<std::string>>;
public:
enum class Direction { To, From, Bidirectional };
void load(cereal::JSONInputArchive &archive_in);
bool contains(const Asset &asset);
GenericConfigId getId() const { return zone_id; }
const std::string & getName() const { return zone_name; }
const std::vector<std::pair<Direction, GenericConfigId>> & getAdjacentZones() const { return adjacent_zones; }
const MatchQuery & getMatchQuery() const { return match_query; }
bool isAnyZone() const { return is_any; }
private:
bool matchAttributes(const AttrData &data);
GenericConfigId zone_id;
std::string zone_name;
std::vector<std::pair<Direction, GenericConfigId>> adjacent_zones;
MatchQuery match_query;
bool is_any;
};
#endif // __ZONE_H__

View File

@@ -0,0 +1,53 @@
// 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 __ZONES_CONFIG_H__
#define __ZONES_CONFIG_H__
#include <vector>
#include <string>
#include <set>
#include <arpa/inet.h>
#include "generic_rulebase_context.h"
#include "match_query.h"
#include "i_generic_rulebase.h"
#include "zone.h"
class Zones
{
public:
void load(cereal::JSONInputArchive &archive_in)
{
cereal::load(archive_in, zones);
}
const std::vector<Zone> & getZones() const { return zones; }
std::vector<Zone> zones;
};
class ZonesConfig : Singleton::Consume<I_GenericRulebase>
{
public:
static void preload();
void load(cereal::JSONInputArchive &archive_in);
const std::vector<Zone> & getZones() const { return zones; }
private:
std::vector<Zone> zones;
};
#endif //__ZONES_CONFIG_H__

View 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 __GRADUAL_DEPLOYMENT_H__
#define __GRADUAL_DEPLOYMENT_H__
#include "i_gradual_deployment.h"
#include "singleton.h"
#include "i_rest_api.h"
#include "i_table.h"
#include "i_mainloop.h"
#include "component.h"
class GradualDeployment
:
public Component,
Singleton::Provide<I_GradualDeployment>,
Singleton::Consume<I_RestApi>,
Singleton::Consume<I_MainLoop>
{
public:
GradualDeployment();
~GradualDeployment();
void init();
private:
class Impl;
std::unique_ptr<Impl> pimpl;
};
#endif // __GRADUAL_DEPLOYMENT_H__

View File

@@ -0,0 +1,45 @@
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef __HEALTH_CHECK_MANAGER_H__
#define __HEALTH_CHECK_MANAGER_H__
#include "singleton.h"
#include "i_health_check_manager.h"
#include "i_mainloop.h"
#include "i_rest_api.h"
#include "component.h"
#include "i_messaging.h"
#include "i_environment.h"
class HealthCheckManager
:
public Component,
Singleton::Provide<I_Health_Check_Manager>,
Singleton::Consume<I_MainLoop>,
Singleton::Consume<I_RestApi>,
Singleton::Consume<I_Messaging>,
Singleton::Consume<I_Environment>
{
public:
HealthCheckManager();
~HealthCheckManager();
void init() override;
private:
class Impl;
std::unique_ptr<Impl> pimpl;
};
#endif // __HEALTH_CHECK_MANAGER_H__

View File

@@ -0,0 +1,44 @@
// 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 __HEALTH_CHECKER_H__
#define __HEALTH_CHECKER_H__
#include "singleton.h"
#include "i_mainloop.h"
#include "i_socket_is.h"
#include "i_health_check_manager.h"
#include "component.h"
class HealthChecker
:
public Component,
Singleton::Consume<I_MainLoop>,
Singleton::Consume<I_Socket>,
Singleton::Consume<I_Health_Check_Manager>
{
public:
HealthChecker();
~HealthChecker();
void init() override;
void fini() override;
void preload() override;
private:
class Impl;
std::unique_ptr<Impl> pimpl;
};
#endif // __HEALTH_CHECKER_H__

View 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.
#ifndef __FILTER_VERDICT_H__
#define __FILTER_VERDICT_H__
#include <memory>
#include "maybe_res.h"
#include "i_http_event_impl.h"
class FilterVerdict
{
public:
FilterVerdict(ngx_http_cp_verdict_e _verdict = ngx_http_cp_verdict_e::TRAFFIC_VERDICT_INSPECT)
:
verdict(_verdict)
{}
FilterVerdict(const EventVerdict &_verdict, ModifiedChunkIndex _event_idx = -1)
:
verdict(_verdict.getVerdict())
{
if (verdict == ngx_http_cp_verdict_e::TRAFFIC_VERDICT_INJECT) {
addModifications(_verdict.getModifications(), _event_idx);
}
}
void
addModifications(const FilterVerdict &other)
{
if (other.verdict != ngx_http_cp_verdict_e::TRAFFIC_VERDICT_INJECT) return;
modifications.insert(modifications.end(), other.modifications.begin(), other.modifications.end());
total_modifications += other.total_modifications;
}
void
addModifications(
const ModificationList &mods,
ModifiedChunkIndex _event_idx,
ngx_http_cp_verdict_e alt_verdict = ngx_http_cp_verdict_e::TRAFFIC_VERDICT_IRRELEVANT)
{
total_modifications += mods.size();
modifications.push_back(EventModifications(_event_idx, mods));
if (alt_verdict != ngx_http_cp_verdict_e::TRAFFIC_VERDICT_IRRELEVANT) verdict = alt_verdict;
}
uint getModificationsAmount() const { return total_modifications; }
ngx_http_cp_verdict_e getVerdict() const { return verdict; }
const std::vector<EventModifications> & getModifications() const { return modifications; }
private:
ngx_http_cp_verdict_e verdict = ngx_http_cp_verdict_e::TRAFFIC_VERDICT_INSPECT;
std::vector<EventModifications> modifications;
uint total_modifications = 0;
};
#endif // __FILTER_VERDICT_H__

View 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.
#ifndef __I_HTTP_EVENT_IMPL_H__
#define __I_HTTP_EVENT_IMPL_H__
#ifndef __HTTP_INSPECTION_EVENTS_H__
#error i_http_event_impl.h should not be included directly!
#endif //__HTTP_INSPECTION_EVENTS_H__
#include <string>
#include <map>
#include <vector>
#include "debug.h"
#include "buffer.h"
#include "http_transaction_data.h"
#include "nginx_attachment_common.h"
USE_DEBUG_FLAG(D_HTTP_MANAGER);
using ModificationType = ngx_http_modification_type_e;
using ModificationPosition = ngx_http_cp_inject_pos_t;
static const ModificationPosition injection_pos_irrelevant = INJECT_POS_IRRELEVANT;
template <typename TMod>
class Modification
{
public:
Modification(const TMod &mod, ModificationType mod_type)
:
Modification(mod, mod_type, injection_pos_irrelevant)
{}
Modification(const TMod &mod, ModificationType mod_type, ModificationPosition mod_position)
:
modification(mod),
type(mod_type),
position(mod_position)
{
dbgAssert(mod_type != ModificationType::APPEND || position == injection_pos_irrelevant)
<< "Injection position is not applicable to a modification of type \"Append\"";
dbgAssert(mod_type != ModificationType::INJECT || position >= 0)
<< "Invalid injection position: must be non-negative. Position: "
<< position;
}
ModificationPosition getModificationPosition() const { return position; }
ModificationType getModificationType() const { return type; }
const TMod & getModification() const { return modification; }
private:
TMod modification;
ModificationType type;
ModificationPosition position;
};
using ModifiedChunkIndex = int;
using ModificationBuffer = std::tuple<ModificationPosition, ModificationType, Buffer>;
using ModificationList = std::vector<ModificationBuffer>;
using EventModifications = std::pair<ModifiedChunkIndex, ModificationList>;
template <typename TMod>
class I_ModifiableContent
{
public:
virtual Maybe<void> modify(const Modification<TMod> &mod) = 0;
virtual ModificationList getModificationList() const = 0;
protected:
virtual ~I_ModifiableContent() {}
};
using HeaderKey = std::string;
using HeaderModification = std::pair<std::pair<ModificationPosition, HeaderKey>, Buffer>;
class HttpHeaderModification : I_ModifiableContent<HeaderModification>
{
public:
Maybe<void>
appendHeader(const HeaderKey &key, const Buffer &value)
{
return modify(
Modification<HeaderModification>(
HeaderModification({ { injection_pos_irrelevant, key }, value }),
ModificationType::APPEND
)
);
}
Maybe<void>
injectValue(ModificationPosition position, const Buffer &data)
{
return modify(
Modification<HeaderModification>(
HeaderModification({ { position, HeaderKey() }, data }),
ModificationType::INJECT,
position
)
);
}
ModificationList
getModificationList() const override
{
ModificationList modification_list;
for (const auto &modification : headers_to_append) {
modification_list.emplace_back(injection_pos_irrelevant, ModificationType::APPEND, modification.first);
modification_list.emplace_back(injection_pos_irrelevant, ModificationType::APPEND, modification.second);
}
for (const auto &modification : header_injections) {
modification_list.emplace_back(modification.first, ModificationType::INJECT, modification.second);
}
return modification_list;
}
private:
Maybe<void>
modify(const Modification<HeaderModification> &mod) override
{
auto modification_type = mod.getModificationType();
switch (modification_type) {
case ModificationType::APPEND: {
const HeaderKey &appended_header_key = mod.getModification().first.second;
auto iterator = headers_to_append.find(appended_header_key);
if (iterator != headers_to_append.end()) {
return
genError(
"Append modification with provided header key already exists. Header key: \"" +
appended_header_key +
"\""
);
}
headers_to_append.emplace(appended_header_key, mod.getModification().second);
break;
}
case ModificationType::INJECT: {
auto iterator = header_injections.find(mod.getModificationPosition());
if (iterator != header_injections.end()) {
return genError("Inject modification with provided position already exists");
}
header_injections.emplace(mod.getModificationPosition(), mod.getModification().second);
break;
}
case ModificationType::REPLACE: {
// future support to pass new Content-Length
dbgWarning(D_HTTP_MANAGER) << "Replace modification is not yet supported";
break;
}
default:
dbgAssert(false)
<< "Unknown type of ModificationType: "
<< static_cast<int>(modification_type);
}
return Maybe<void>();
}
private:
std::map<HeaderKey, Buffer> headers_to_append;
std::map<ModificationPosition, Buffer> header_injections;
};
class HttpHeader
{
public:
HttpHeader() = default;
HttpHeader(const Buffer &_key, const Buffer &_value, uint8_t _header_index, bool _is_last_header = false)
:
key(_key),
value(_value),
is_last_header(_is_last_header),
header_index(_header_index)
{
}
// LCOV_EXCL_START - sync functions, can only be tested once the sync module exists
template <class Archive>
void
save(Archive &ar) const
{
ar(
key,
value,
is_last_header,
header_index
);
}
template <class Archive>
void
load(Archive &ar)
{
ar(
key,
value,
is_last_header,
header_index
);
}
// LCOV_EXCL_STOP
void
print(std::ostream &out_stream) const
{
out_stream
<< "'"
<< std::dumpHex(key)
<< "': '"
<< std::dumpHex(value)
<< "' (Index: "
<< std::to_string(header_index)
<< ", Is last header: "
<< (is_last_header ? "True" : "False")
<< ")";
}
const Buffer & getKey() const { return key; }
const Buffer & getValue() const { return value; }
bool isLastHeader() const { return is_last_header; }
uint8_t getHeaderIndex() const { return header_index; }
private:
Buffer key;
Buffer value;
bool is_last_header = false;
uint8_t header_index = 0;
};
using BodyModification = Buffer;
class HttpBodyModification : I_ModifiableContent<BodyModification>
{
public:
Maybe<void>
inject(ModificationPosition position, const Buffer &data)
{
return modify(
Modification<BodyModification>(
std::move(data),
ModificationType::INJECT,
position
)
);
}
ModificationList
getModificationList() const override
{
ModificationList injected_data;
for (const auto &injection : modifications) {
auto injection_buffer = injection.second;
injected_data.emplace_back(injection.first, ModificationType::INJECT, injection_buffer);
}
return injected_data;
}
private:
Maybe<void>
modify(const Modification<BodyModification> &mod) override
{
if (modifications.find(mod.getModificationPosition()) != modifications.end()) {
return genError("Modification at the provided index already exists");
}
modifications[mod.getModificationPosition()] = mod.getModification();
return Maybe<void>();
}
std::map<ModificationPosition, Buffer> modifications;
};
class HttpBody
{
public:
HttpBody()
:
data(),
previous_chunked_data(),
is_last_chunk(false),
body_chunk_index(0)
{}
HttpBody(const Buffer &body_data, bool _is_last_chunk, uint8_t _body_chunk_index)
:
data(body_data),
previous_chunked_data(),
is_last_chunk(_is_last_chunk),
body_chunk_index(_body_chunk_index)
{}
// LCOV_EXCL_START - sync functions, can only be tested once the sync module exists
template <class Archive>
void
save(Archive &ar) const
{
ar(
data,
previous_chunked_data,
is_last_chunk,
body_chunk_index
);
}
template <class Archive>
void
load(Archive &ar)
{
ar(
data,
previous_chunked_data,
is_last_chunk,
body_chunk_index
);
}
// LCOV_EXCL_STOP
void
print(std::ostream &out_stream) const
{
out_stream
<< "'"
<< std::dumpHex(data)
<< "' (Index: "
<< std::to_string(body_chunk_index)
<< ", Is last chunk: "
<< (is_last_chunk ? "True" : "False")
<< ")";
}
const Buffer & getData() const { return data; }
const Buffer & getPreviousChunkedData() const { return previous_chunked_data; }
void setPreviousChunkedData(const Buffer &prev_body_data) { previous_chunked_data = prev_body_data; }
bool isLastChunk() const { return is_last_chunk; }
uint8_t getBodyChunkIndex() const { return body_chunk_index; }
private:
Buffer data;
Buffer previous_chunked_data;
bool is_last_chunk;
uint8_t body_chunk_index;
};
class EventVerdict
{
public:
EventVerdict() = default;
EventVerdict(ngx_http_cp_verdict_e event_verdict) : modifications(), verdict(event_verdict) {}
EventVerdict(const ModificationList &mods) : modifications(mods) {}
EventVerdict(const ModificationList &mods, ngx_http_cp_verdict_e event_verdict) :
modifications(mods),
verdict(event_verdict)
{}
// LCOV_EXCL_START - sync functions, can only be tested once the sync module exists
template <typename T> void serialize(T &ar, uint) { ar(verdict); }
// LCOV_EXCL_STOP
const ModificationList & getModifications() const { return modifications; }
ngx_http_cp_verdict_e getVerdict() const { return verdict; }
private:
ModificationList modifications;
ngx_http_cp_verdict_e verdict = ngx_http_cp_verdict_e::TRAFFIC_VERDICT_INSPECT;
};
#endif // __I_HTTP_EVENT_IMPL_H__

View File

@@ -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 __HTTP_INSPECTION_EVENTS_H__
#define __HTTP_INSPECTION_EVENTS_H__
#include "debug.h"
#include "event.h"
#include "http_event_impl/filter_verdict.h"
#include "http_event_impl/i_http_event_impl.h"
using ResponseCode = uint16_t;
class HttpRequestHeaderEvent : public Event<HttpRequestHeaderEvent, EventVerdict>
{
public:
HttpRequestHeaderEvent(const HttpHeader &header) : req_header(header) {}
const Buffer & getKey() const { return req_header.getKey(); }
const Buffer & getValue() const { return req_header.getValue(); }
bool isLastHeader() const { return req_header.isLastHeader(); }
uint8_t getHeaderIndex() const { return req_header.getHeaderIndex(); }
template <class Archive>
void
save(Archive &ar) const
{
req_header.save(ar);
}
void print(std::ostream &out_stream) const { req_header.print(out_stream); }
private:
const HttpHeader &req_header;
};
class HttpResponseHeaderEvent: public Event<HttpResponseHeaderEvent, EventVerdict>
{
public:
HttpResponseHeaderEvent(const HttpHeader &header) : res_header(header) {}
const Buffer & getKey() const { return res_header.getKey(); }
const Buffer & getValue() const { return res_header.getValue(); }
bool isLastHeader() const { return res_header.isLastHeader(); }
uint8_t getHeaderIndex() const { return res_header.getHeaderIndex(); }
template <class Archive>
void
save(Archive &ar) const
{
res_header.save(ar);
}
void print(std::ostream &out_stream) const { res_header.print(out_stream); }
private:
const HttpHeader &res_header;
};
class HttpRequestBodyEvent: public Event<HttpRequestBodyEvent, EventVerdict>
{
public:
HttpRequestBodyEvent(const HttpBody &body, const Buffer &previous_chunked_data)
:
req_body(body),
prev_chunked_data(previous_chunked_data)
{}
const Buffer & getData() const { return req_body.getData(); }
const Buffer & getPreviousChunkedData() const { return prev_chunked_data; }
bool isLastChunk() const { return req_body.isLastChunk(); }
template <class Archive>
void
save(Archive &ar) const
{
req_body.save(ar);
}
void print(std::ostream &out_stream) const { req_body.print(out_stream); }
private:
const HttpBody &req_body;
const Buffer &prev_chunked_data;
};
class HttpResponseBodyEvent: public Event<HttpResponseBodyEvent, EventVerdict>
{
public:
HttpResponseBodyEvent(const HttpBody &body, const Buffer &previous_chunked_data)
:
res_body(body),
prev_chunked_data(previous_chunked_data)
{}
const Buffer & getData() const { return res_body.getData(); }
const Buffer & getPreviousChunkedData() const { return prev_chunked_data; }
bool isLastChunk() const { return res_body.isLastChunk(); }
uint8_t getBodyChunkIndex() const { return res_body.getBodyChunkIndex(); }
template <class Archive>
void
save(Archive &ar) const
{
res_body.save(ar);
}
void print(std::ostream &out_stream) const { res_body.print(out_stream); }
private:
const HttpBody &res_body;
const Buffer &prev_chunked_data;
};
class NewHttpTransactionEvent : public Event<NewHttpTransactionEvent, EventVerdict>
{
public:
NewHttpTransactionEvent(const HttpTransactionData &event_data) : http_transaction_event_data(event_data) {}
const IPAddr & getSourceIP() const { return http_transaction_event_data.getSourceIP(); }
uint16_t getSourcePort() const { return http_transaction_event_data.getSourcePort(); }
const IPAddr & getListeningIP() const { return http_transaction_event_data.getListeningIP(); }
uint16_t getListeningPort() const { return http_transaction_event_data.getListeningPort(); }
const std::string & getDestinationHost() const { return http_transaction_event_data.getDestinationHost(); }
const std::string & getHttpProtocol() const { return http_transaction_event_data.getHttpProtocol(); }
const std::string & getURI() const { return http_transaction_event_data.getURI(); }
const std::string & getHttpMethod() const { return http_transaction_event_data.getHttpMethod(); }
void print(std::ostream &out_stream) const { http_transaction_event_data.print(out_stream); }
template <class Archive>
void
save(Archive &ar) const
{
http_transaction_event_data.save(ar);
}
private:
const HttpTransactionData &http_transaction_event_data;
};
class ResponseCodeEvent : public Event<ResponseCodeEvent, EventVerdict>
{
public:
ResponseCodeEvent(const ResponseCode &res_code) : http_response_code(res_code) {}
const ResponseCode & getResponseCode() const { return http_response_code; }
template <class Archive>
void
save(Archive &ar) const
{
ar(http_response_code);
}
void print(std::ostream &out_stream) const { out_stream << http_response_code; }
private:
ResponseCode http_response_code;
};
class EndRequestEvent : public Event<EndRequestEvent, EventVerdict>
{
};
class EndTransactionEvent : public Event<EndTransactionEvent, EventVerdict>
{
};
class WaitTransactionEvent : public Event<WaitTransactionEvent, EventVerdict>
{
};
#endif // __HTTP_INSPECTION_EVENTS_H__

View File

@@ -0,0 +1,47 @@
// 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_MANAGER_H__
#define __HTTP_MANAGER_H__
#include "singleton.h"
#include "i_mainloop.h"
#include "i_http_manager.h"
#include "i_rest_api.h"
#include "i_table.h"
#include "i_logging.h"
#include "component.h"
class HttpManager
:
public Component,
Singleton::Provide<I_HttpManager>,
Singleton::Consume<I_Table>,
Singleton::Consume<I_MainLoop>,
Singleton::Consume<I_Logging>,
Singleton::Consume<I_TimeGet>
{
public:
HttpManager();
~HttpManager();
void preload();
void init();
private:
class Impl;
std::unique_ptr<Impl> pimpl;
};
#endif // __HTTP_MANAGER_H__

View File

@@ -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 __HTTP_TRANSACTION_ENUM_H__
#define __HTTP_TRANSACTION_ENUM_H__
namespace HttpTransaction {
enum class Method { GET, HEAD, POST, DELETE, CONNECT, OPTIONS, TRACE, PATCH, PUT };
enum class Dir { REQUEST, RESPONSE };
enum class Verdict { ACCEPT, DROP, INJECT, REDIRECT, NONE, DEFAULT };
enum class StatusCode {
OK = 200,
CREATED = 201,
NO_CONTENT = 204,
NOT_MODIFIED = 304,
BAD_REQUEST = 400,
UNAUTHORIZED = 401,
FORBIDDEN = 403,
NOT_FOUND = 404,
CONFLICT = 409,
INTERNAL_SERVER_ERROR = 500
};
}
#endif // __HTTP_TRANSACTION_ENUM_H__

View File

@@ -0,0 +1,136 @@
// 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_TRANSACTION_DATA_H__
#define __HTTP_TRANSACTION_DATA_H__
#include <iostream>
#include <string>
#include <list>
#include <string>
#include <vector>
#include "connkey.h"
#include "buffer.h"
#include "enum_range.h"
#include "maybe_res.h"
#include "http_transaction_common.h"
#include "compression_utils.h"
class HttpTransactionData
{
public:
HttpTransactionData();
HttpTransactionData (
std::string http_proto,
std::string method,
std::string host_name,
IPAddr listening_ip,
uint16_t listening_port,
std::string uri,
IPAddr client_ip,
uint16_t client_port
);
// LCOV_EXCL_START - sync functions, can only be tested once the sync module exists
template <class Archive>
void
save(Archive &ar) const
{
ar(
http_proto,
method,
host_name,
listening_ip,
listening_port,
uri,
client_ip,
client_port,
response_content_encoding
);
}
template <class Archive>
void
load(Archive &ar)
{
ar(
http_proto,
method,
host_name,
listening_ip,
listening_port,
uri,
client_ip,
client_port,
response_content_encoding
);
}
// LCOV_EXCL_STOP
static Maybe<HttpTransactionData> createTransactionData(const Buffer &transaction_raw_data);
const IPAddr & getSourceIP() const { return client_ip; }
uint16_t getSourcePort() const { return client_port; }
const IPAddr & getListeningIP() const { return listening_ip; }
uint16_t getListeningPort() const { return listening_port; }
const std::string & getDestinationHost() const { return host_name; }
const std::string & getHttpProtocol() const { return http_proto; }
const std::string & getURI() const { return uri; }
const std::string & getHttpMethod() const { return method; }
void print(std::ostream &out_stream) const;
CompressionType getResponseContentEncoding() const { return response_content_encoding; }
bool isRequest() const { return is_request; }
void setDirection(bool _is_request) { is_request = _is_request; }
void
setResponseContentEncoding(const CompressionType _response_content_encoding)
{
response_content_encoding = _response_content_encoding;
}
static const std::string http_proto_ctx;
static const std::string method_ctx;
static const std::string host_name_ctx;
static const std::string listening_port_ctx;
static const std::string listening_ip_ctx;
static const std::string uri_ctx;
static const std::string uri_path_decoded;
static const std::string uri_query_decoded;
static const std::string client_ip_ctx;
static const std::string client_port_ctx;
static const std::string req_headers;
static const std::string req_body;
static const std::string source_identifier;
static const std::string proxy_ip_ctx;
static const CompressionType default_response_content_encoding;
private:
std::string http_proto;
std::string method = "GET";
std::string host_name;
IPAddr listening_ip;
uint16_t listening_port;
std::string uri;
IPAddr client_ip;
uint16_t client_port;
bool is_request;
CompressionType response_content_encoding;
};
#endif // __HTTP_TRANSACTION_DATA_H__

View File

@@ -0,0 +1,34 @@
// 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_MODE_TELEMETRY_H__
#define __HYBRID_MODE_TELEMETRY_H__
#include "generic_metric.h"
class HybridModeMetricEvent : public Event<HybridModeMetricEvent>
{
public:
HybridModeMetricEvent() {}
};
class HybridModeMetric : public GenericMetric, public Listener<HybridModeMetricEvent>
{
public:
void upon(const HybridModeMetricEvent &event) override;
private:
MetricCalculations::LastReportedValue<int> wd_process_restart{this, "watchdogProcessStartupEventsSum"};
};
#endif // __HYBRID_MODE_TELEMETRY_H__

View 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 __I_DETAILS_RESOLVER_H__
#define __I_DETAILS_RESOLVER_H__
#include "maybe_res.h"
#include <string>
class I_DetailsResolver
{
public:
virtual Maybe<std::string> getHostname() = 0;
virtual Maybe<std::string> getPlatform() = 0;
virtual Maybe<std::string> getArch() = 0;
virtual std::string getAgentVersion() = 0;
virtual bool isKernelVersion3OrHigher() = 0;
virtual bool isGwNotVsx() = 0;
virtual bool isVersionEqualOrAboveR8110() = 0;
virtual bool isReverseProxy() = 0;
virtual Maybe<std::tuple<std::string, std::string, std::string>> parseNginxMetadata() = 0;
virtual std::map<std::string, std::string> getResolvedDetails() = 0;
#if defined(gaia) || defined(smb)
virtual bool compareCheckpointVersion(int cp_version, std::function<bool(int, int)> compare_operator) const = 0;
#endif // gaia || smb
protected:
virtual ~I_DetailsResolver() {}
};
#endif // __I_DETAILS_RESOLVER_H__

View File

@@ -0,0 +1,44 @@
// 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_DOWNLOADER_H__
#define __I_DOWNLOADER_H__
#include "i_orchestration_tools.h"
#include "i_update_communication.h"
#include <string>
class I_Downloader
{
public:
virtual Maybe<std::string> downloadFileFromFog(
const std::string &checksum,
Package::ChecksumTypes,
const GetResourceFile &resourse_file
) const = 0;
virtual Maybe<std::map<std::string, std::string>>downloadVirtualFileFromFog(
const GetResourceFile &resourse_file,
Package::ChecksumTypes checksum_type
) const = 0;
virtual Maybe<std::string> downloadFileFromURL(
const std::string &url,
const std::string &checksum,
Package::ChecksumTypes checksum_type,
const std::string &service_name
) const = 0;
};
#endif // __I_DOWNLOADER_H__

View File

@@ -0,0 +1,60 @@
// 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_EXTERNAL_SDK_SERVER_H__
#define __I_EXTERNAL_SDK_SERVER_H__
#include <string>
#include <map>
#include "report/report.h"
#include "debug.h"
class I_ExternalSdkServer
{
public:
virtual void
sendLog(
const std::string &event_name,
ReportIS::Audience audience,
ReportIS::Severity severity,
ReportIS::Priority priority,
const std::string &tag,
const std::map<std::string, std::string> &additional_fields) = 0;
virtual void
sendDebug(
const std::string &file_name,
const std::string &function_name,
unsigned int line_number,
Debug::DebugLevel debug_level,
const std::string &trace_id,
const std::string &span_id,
const std::string &message,
const std::map<std::string, std::string> &additional_fields) = 0;
virtual void
sendMetric(
const std::string &event_title,
const std::string &service_name,
ReportIS::AudienceTeam team,
ReportIS::IssuingEngine issuing_engine,
const std::map<std::string, std::string> &additional_fields) = 0;
virtual Maybe<std::string> getConfigValue(const std::string &config_path) = 0;
protected:
virtual ~I_ExternalSdkServer() {}
};
#endif // __I_EXTERNAL_SDK_SERVER_H__

View File

@@ -0,0 +1,36 @@
// 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_GENERIC_RULEBASE_H__
#define __I_GENERIC_RULEBASE_H__
#include <vector>
#include "generic_rulebase/parameters_config.h"
#include "generic_rulebase/zone.h"
#include "config.h"
class I_GenericRulebase
{
public:
virtual Maybe<Zone, Config::Errors> getLocalZone() const = 0;
virtual Maybe<Zone, Config::Errors> getOtherZone() const = 0;
using ParameterKeyValues = std::unordered_map<std::string, std::set<std::string>>;
virtual std::set<ParameterBehavior> getBehavior(const ParameterKeyValues &key_value_pairs) const = 0;
protected:
~I_GenericRulebase() {}
};
#endif // __I_GENERIC_RULEBASE_H__

View File

@@ -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 __I_GRADUAL_DEPLOYMENT_H__
#define __I_GRADUAL_DEPLOYMENT_H__
#include <memory>
#include <vector>
#include <arpa/inet.h>
#include "maybe_res.h"
#include "c_common/ip_common.h"
class I_GradualDeployment
{
public:
enum class AttachmentType { NGINX, KERNEL, COUNT };
virtual Maybe<void> setPolicy(AttachmentType type, const std::vector<std::string> &str_ip_ranges) = 0;
virtual std::vector<std::string> getPolicy(AttachmentType type) = 0;
virtual std::vector<IPRange> & getParsedPolicy(AttachmentType type) = 0;
protected:
virtual ~I_GradualDeployment() {}
};
#endif // __I_GRADUAL_DEPLOYMENT_H__

View File

@@ -0,0 +1,34 @@
// 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_HTTP_MANAGER_H__
#define __I_HTTP_MANAGER_H__
#include "http_inspection_events.h"
class I_HttpManager
{
public:
virtual FilterVerdict inspect(const HttpTransactionData &event) = 0;
virtual FilterVerdict inspect(const HttpHeader &event, bool is_request) = 0;
virtual FilterVerdict inspect(const HttpBody &event, bool is_request) = 0;
virtual FilterVerdict inspect(const ResponseCode &event) = 0;
virtual FilterVerdict inspectEndRequest() = 0;
virtual FilterVerdict inspectEndTransaction() = 0;
virtual FilterVerdict inspectDelayedVerdict() = 0;
protected:
virtual ~I_HttpManager() {}
};
#endif // __I_HTTP_MANAGER_H__

View File

@@ -0,0 +1,27 @@
// 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_K8S_POLICY_GEN_H__
#define __I_K8S_POLICY_GEN_H__
class I_K8S_Policy_Gen
{
public:
virtual std::string parsePolicy(const std::string &policy_version) = 0;
virtual const std::string & getPolicyPath(void) const = 0;
protected:
~I_K8S_Policy_Gen() {}
};
#endif //__I_K8S_POLICY_GEN_H__

View 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_MANIFEST_CONTROLLER_H__
#define __I_MANIFEST_CONTROLLER_H__
#include <string>
class I_ManifestController
{
public:
virtual bool updateManifest(const std::string &new_manifest_file) = 0;
virtual bool loadAfterSelfUpdate() = 0;
};
#endif // __I_MANIFEST_CONTROLLER_H__

View File

@@ -0,0 +1,87 @@
// 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_ORCHESTRATION_STATUS_H__
#define __I_ORCHESTRATION_STATUS_H__
#include <string>
#include <chrono>
#include <cereal/archives/json.hpp>
#include "enum_array.h"
enum class OrchestrationStatusResult { SUCCESS, FAILED };
enum class OrchestrationStatusFieldType { REGISTRATION, MANIFEST, LAST_UPDATE, COUNT };
enum class OrchestrationStatusConfigType { MANIFEST, POLICY, SETTINGS, DATA, COUNT };
class I_OrchestrationStatus
{
public:
virtual void writeStatusToFile() = 0;
virtual const std::string & getLastUpdateAttempt() const = 0;
virtual const std::string & getUpdateStatus() const = 0;
virtual const std::string & getUpdateTime() const = 0;
virtual const std::string & getLastManifestUpdate() const = 0;
virtual const std::string & getPolicyVersion() const = 0;
virtual const std::string & getLastPolicyUpdate() const = 0;
virtual const std::string & getLastSettingsUpdate() const = 0;
virtual const std::string & getUpgradeMode() const = 0;
virtual const std::string & getFogAddress() const = 0;
virtual const std::string & getRegistrationStatus() const = 0;
virtual const std::string & getAgentId() const = 0;
virtual const std::string & getProfileId() const = 0;
virtual const std::string & getTenantId() const = 0;
virtual const std::string & getManifestStatus() const = 0;
virtual const std::string & getManifestError() const = 0;
virtual const std::map<std::string, std::string> & getServicePolicies() const = 0;
virtual const std::map<std::string, std::string> & getServiceSettings() const = 0;
virtual const std::string getRegistrationDetails() const = 0;
virtual void recoverFields() = 0;
virtual void setIsConfigurationUpdated(EnumArray<OrchestrationStatusConfigType, bool> config_types) = 0;
virtual void setFogAddress(const std::string &_fog_address) = 0;
virtual void setLastUpdateAttempt() = 0;
virtual void setPolicyVersion(const std::string &_policy_version) = 0;
virtual void setRegistrationStatus(const std::string &_reg_status) = 0;
virtual void setUpgradeMode(const std::string &_upgrade_mode) = 0;
virtual void setAgentType(const std::string &_agent_type) = 0;
virtual void setAgentDetails(
const std::string &_agent_id,
const std::string &_profile_id,
const std::string &_tenant_id
) = 0;
virtual void
setFieldStatus(
const OrchestrationStatusFieldType &field_type_status,
const OrchestrationStatusResult &status,
const std::string &failure_reason = ""
) = 0;
virtual void
setRegistrationDetails(
const std::string &name,
const std::string &type,
const std::string &platform,
const std::string &arch
) = 0;
virtual void
setServiceConfiguration(
const std::string &service_name,
const std::string &path,
const OrchestrationStatusConfigType &configuration_file_type
) = 0;
};
#endif // __I_ORCHESTRATION_STATUS_H__

View File

@@ -0,0 +1,125 @@
// 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_ORCHESTRATION_TOOLS_H__
#define __I_ORCHESTRATION_TOOLS_H__
#include "package.h"
#include "debug.h"
#include "maybe_res.h"
#include <fstream>
USE_DEBUG_FLAG(D_ORCHESTRATOR);
class I_OrchestrationTools
{
public:
// Used for the calculation of the manifest and the policy files
static const Package::ChecksumTypes SELECTED_CHECKSUM_TYPE = Package::ChecksumTypes::SHA256;
static constexpr const char * SELECTED_CHECKSUM_TYPE_STR = "sha256sum";
using packageName = std::string;
using packageDetails = std::string;
template<class T>
Maybe<T>
jsonFileToObject(const std::string &file_path) const
{
Maybe<std::string> file_data = readFile(file_path);
if (file_data.ok()) {
return jsonStringToObject<T>(file_data.unpack());
}
return genError(file_data.getErr());
}
template<class T>
Maybe<T>
jsonStringToObject(const std::string &input) const
{
std::stringstream string_stream;
string_stream << input;
return jsonStringToObject<T>(string_stream);
}
template<class T>
Maybe<T>
jsonStringToObject(std::stringstream &string_stream) const
{
try {
cereal::JSONInputArchive archive_in(string_stream);
T object;
object.serialize(archive_in);
return object;
} catch (cereal::Exception &e) {
return genError(e.what());
}
}
template<class T>
bool
objectToJsonFile(T &obj, const std::string &file_path) const
{
try {
std::ofstream ostream(file_path);
cereal::JSONOutputArchive archive_out(ostream);
obj.serialize(archive_out);
} catch (cereal::Exception &e) {
dbgWarning(D_ORCHESTRATOR) << "Failed to write object to JSON file. Object: " << typeid(T).name()
<< ", file : "<< file_path << ", error: " << e.what();
return false;
}
return true;
}
template<class T>
Maybe<std::string>
objectToJson(const T &obj) const
{
std::stringstream sstream;
try {
cereal::JSONOutputArchive archive_out(sstream);
obj.serialize(archive_out);
} catch (cereal::Exception &e) {
std::string error_msg = "Failed to write object to JSON. Object: " + std::string(typeid(T).name())
+ ", error: " + e.what();
return genError(error_msg);
}
return sstream.str();
}
virtual bool packagesToJsonFile(const std::map<packageName, Package> &packages, const std::string &path) const = 0;
virtual Maybe<std::map<packageName, Package>> loadPackagesFromJson(const std::string &path) const = 0;
virtual Maybe<std::map<packageName, packageDetails>> jsonObjectSplitter(
const std::string &json,
const std::string &tenant_id = "") const = 0;
virtual bool isNonEmptyFile(const std::string &path) const = 0;
virtual Maybe<std::string> readFile(const std::string &path) const = 0;
virtual bool writeFile(const std::string &text, const std::string &path) const = 0;
virtual bool removeFile(const std::string &path) const = 0;
virtual bool copyFile(const std::string &src_path, const std::string &dst_path) const = 0;
virtual bool doesFileExist(const std::string &file_path) const = 0;
virtual bool createDirectory(const std::string &directory_path) const = 0;
virtual bool doesDirectoryExist(const std::string &dir_path) const = 0;
virtual bool executeCmd(const std::string &cmd) const = 0;
virtual std::string base64Encode(const std::string &input) const = 0;
virtual std::string base64Decode(const std::string &input) const = 0;
virtual Maybe<std::string> calculateChecksum(
Package::ChecksumTypes checksum_type,
const std::string &path) const = 0;
};
#endif // __I_ORCHESTRATION_TOOLS_H__

View File

@@ -0,0 +1,47 @@
// 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_PACKAGE_HANDLER_H__
#define __I_PACKAGE_HANDLER_H__
#include <string>
class I_PackageHandler
{
public:
virtual bool shouldInstallPackage(const std::string &package_name, const std::string &install_file_path) const = 0;
virtual bool installPackage(
const std::string &package_name,
const std::string &install_file_path,
bool restore_mode
) const = 0;
virtual bool uninstallPackage(
const std::string &package_name,
const std::string &package_path,
const std::string &install_file_path
) const = 0;
virtual bool preInstallPackage(
const std::string &package_name,
const std::string &install_file_path
) const = 0;
virtual bool postInstallPackage(
const std::string &package_name,
const std::string &install_file_path
) const = 0;
virtual bool updateSavedPackage(
const std::string &package_name,
const std::string &install_file_path
) const = 0;
};
#endif // __I_PACKAGE_HANDLER_H__

Some files were not shown because too many files have changed in this diff Show More