mirror of
https://github.com/openappsec/openappsec.git
synced 2025-09-29 19:24:26 +03:00
First release of open-appsec source code
This commit is contained in:
7
components/gradual_deployment/CMakeLists.txt
Normal file
7
components/gradual_deployment/CMakeLists.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
add_definitions(-DUSERSPACE)
|
||||
|
||||
include_directories(include)
|
||||
|
||||
add_library(gradual_deployment gradual_deployment.cc )
|
||||
|
||||
add_subdirectory(gradual_deployment_ut)
|
227
components/gradual_deployment/gradual_deployment.cc
Normal file
227
components/gradual_deployment/gradual_deployment.cc
Normal 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(); }
|
@@ -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;"
|
||||
)
|
@@ -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);
|
||||
}
|
Reference in New Issue
Block a user