mirror of
https://github.com/openappsec/openappsec.git
synced 2025-11-16 01:12:18 +03:00
Aug 08 2025 dev (#336)
* sync code * sync code * sync code --------- Co-authored-by: Ned Wright <nedwright@proton.me>
This commit is contained in:
@@ -8,7 +8,7 @@ link_directories(${CMAKE_BINARY_DIR}/core)
|
||||
link_directories(${CMAKE_BINARY_DIR}/core/compression)
|
||||
|
||||
SET(EXECUTABLE_NAME "nginx_conf_collector_bin")
|
||||
add_executable(${EXECUTABLE_NAME} nginx_conf_collector.cc)
|
||||
add_executable(${EXECUTABLE_NAME} nginx_conf_collector.cc fog_connection.cc)
|
||||
target_compile_definitions(${EXECUTABLE_NAME} PRIVATE "NGINX_CONF_COLLECTOR_VERSION=\"$ENV{CI_PIPELINE_ID}\"")
|
||||
|
||||
target_link_libraries(${EXECUTABLE_NAME}
|
||||
@@ -26,6 +26,7 @@ target_link_libraries(${EXECUTABLE_NAME}
|
||||
report
|
||||
config
|
||||
environment
|
||||
curl_http_client
|
||||
singleton
|
||||
rest
|
||||
boost_context
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
#include "fog_connection.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <cereal/archives/json.hpp>
|
||||
#include <cereal/types/vector.hpp>
|
||||
#include <cereal/types/map.hpp>
|
||||
#include <cereal/types/string.hpp>
|
||||
#include "debug.h"
|
||||
#include "internal/curl_http_client.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
USE_DEBUG_FLAG(D_NGINX_MANAGER);
|
||||
|
||||
// Helper function to check if HTTPResponse indicates success
|
||||
bool
|
||||
isSuccessfulResponse(const HTTPResponse& response)
|
||||
{
|
||||
HTTPStatusCode code = response.getHTTPStatusCode();
|
||||
return (code == HTTPStatusCode::HTTP_OK ||
|
||||
code == HTTPStatusCode::HTTP_NO_CONTENT ||
|
||||
code == HTTPStatusCode::HTTP_MULTI_STATUS);
|
||||
}
|
||||
|
||||
FogConnection::FogConnection(const string& token, const string& fog_address)
|
||||
: var_token(token), var_fog(fog_address), curl_client(std::make_unique<CurlHttpClient>()) {}
|
||||
|
||||
void
|
||||
FogConnection::setProxy(const string& hosts)
|
||||
{
|
||||
curl_client->setProxy(hosts);
|
||||
}
|
||||
|
||||
Maybe<void>
|
||||
FogConnection::getCredentials()
|
||||
{
|
||||
AgentRegistrationRequest request;
|
||||
AgentRegistrationRequest::AuthData auth;
|
||||
|
||||
auth.authenticationMethod = "token";
|
||||
auth.data = var_token;
|
||||
|
||||
request.authenticationData.push_back(auth);
|
||||
request.metaData.agentName = "ConfCollector";
|
||||
request.metaData.agentType = "Embedded";
|
||||
request.metaData.platform = "linux";
|
||||
request.metaData.architecture = "x86";
|
||||
request.metaData.additionalMetaData["agentVendor"] = "nginx-conf-collector";
|
||||
|
||||
stringstream ss_req;
|
||||
{
|
||||
cereal::JSONOutputArchive ar(ss_req);
|
||||
request.serialize(ar);
|
||||
}
|
||||
|
||||
dbgTrace(D_NGINX_MANAGER) << "Registration JSON: " << ss_req.str();
|
||||
|
||||
string url = var_fog + "/agents";
|
||||
map<string, string> headers = {{"Content-Type", "application/json"},
|
||||
{"User-Agent", "Infinity Next (a7030abf93a4c13)"}};
|
||||
|
||||
auto response = curl_client->post(url, ss_req.str(), headers);
|
||||
|
||||
dbgTrace(D_NGINX_MANAGER)
|
||||
<< "Register agent response code: "
|
||||
<< static_cast<int>(response.getHTTPStatusCode())
|
||||
<< ", body: "
|
||||
<< response.getBody();
|
||||
|
||||
if (!isSuccessfulResponse(response)) {
|
||||
return genError("Failed to register agent: HTTP "
|
||||
+ to_string(static_cast<int>(response.getHTTPStatusCode()))
|
||||
+ " - "
|
||||
+ response.getBody());
|
||||
}
|
||||
|
||||
if (response.getBody().find("referenceId") != string::npos) {
|
||||
return genError("Registration failed: " + response.getBody());
|
||||
}
|
||||
|
||||
try {
|
||||
AgentRegistrationResponse reg_response;
|
||||
stringstream ss_res(response.getBody());
|
||||
cereal::JSONInputArchive ar(ss_res);
|
||||
reg_response.serialize(ar);
|
||||
|
||||
agent_id = reg_response.agentId;
|
||||
clientId = reg_response.clientId;
|
||||
clientSecret = reg_response.clientSecret;
|
||||
tenant_id = reg_response.tenantId;
|
||||
profile_id = reg_response.profileId;
|
||||
} catch (const exception& e) {
|
||||
dbgTrace(D_NGINX_MANAGER) << "Failed to parse registration response: " << response.getBody();
|
||||
return genError("Failed to parse registration response: " + string(e.what()));
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
Maybe<void>
|
||||
FogConnection::getJWT()
|
||||
{
|
||||
TokenRequest request;
|
||||
request.login = clientId;
|
||||
request.password = clientSecret;
|
||||
|
||||
stringstream ss_req;
|
||||
{
|
||||
cereal::JSONOutputArchive ar(ss_req);
|
||||
ar(request);
|
||||
}
|
||||
|
||||
string url = var_fog + "/oauth/token?grant_type=client_credentials";
|
||||
map<string, string> headers = {{"Content-Type", "application/json"},
|
||||
{"User-Agent", "Infinity Next (a7030abf93a4c13)"}};
|
||||
|
||||
dbgTrace(D_NGINX_MANAGER) << "get JWT JSON: " << ss_req.str();
|
||||
|
||||
curl_client->setBasicAuth(clientId, clientSecret);
|
||||
curl_client->authEnabled(true);
|
||||
auto response = curl_client->post(url, ss_req.str(), headers);
|
||||
|
||||
dbgTrace(D_NGINX_MANAGER)
|
||||
<< "get JWT response code: "
|
||||
<< static_cast<int>(response.getHTTPStatusCode())
|
||||
<< ", body: "
|
||||
<< response.getBody();
|
||||
|
||||
if (!isSuccessfulResponse(response)) {
|
||||
return genError("Failed to get JWT: HTTP "
|
||||
+ to_string(static_cast<int>(response.getHTTPStatusCode()))
|
||||
+ " - "
|
||||
+ response.getBody());
|
||||
}
|
||||
|
||||
if (response.getBody().find("referenceId") != string::npos) {
|
||||
return genError("JWT request failed: " + response.getBody());
|
||||
}
|
||||
|
||||
try {
|
||||
TokenResponse token_response;
|
||||
stringstream ss_res(response.getBody());
|
||||
cereal::JSONInputArchive ar(ss_res);
|
||||
token_response.serialize(ar);
|
||||
|
||||
ra_token = token_response.access_token;
|
||||
} catch (const exception& e) {
|
||||
dbgTrace(D_NGINX_MANAGER) << "Failed to parse JWT response: " << response.getBody();
|
||||
return genError("Failed to parse JWT response: " + string(e.what()));
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
Maybe<void>
|
||||
FogConnection::uploadNginxConfig(const string& config_file_path)
|
||||
{
|
||||
if (tenant_id.empty() || profile_id.empty() || ra_token.empty()) {
|
||||
return genError("Missing required data for upload: tenant_id, profile_id, or ra_token");
|
||||
}
|
||||
|
||||
ifstream file(config_file_path, ios::binary);
|
||||
if (!file.is_open()) {
|
||||
return genError("Cannot open file: " + config_file_path);
|
||||
}
|
||||
|
||||
string file_content((istreambuf_iterator<char>(file)), istreambuf_iterator<char>());
|
||||
file.close();
|
||||
|
||||
if (file_content.empty()) {
|
||||
dbgTrace(D_NGINX_MANAGER) << "Warning: Uploading empty file content from " << config_file_path;
|
||||
}
|
||||
|
||||
|
||||
string upload_url = var_fog + "/agents-core/storage/" + tenant_id + "/nginx/" + profile_id + "/1/nginx.conf";
|
||||
map<string, string> headers = {
|
||||
{"Authorization", "Bearer " + ra_token},
|
||||
{"Content-Type", "text/plain"},
|
||||
{"User-Agent", "Infinity Next (a7030abf93a4c13)"}
|
||||
};
|
||||
|
||||
auto response = curl_client->put(upload_url, file_content, headers);
|
||||
|
||||
dbgTrace(D_NGINX_MANAGER)
|
||||
<< "Upload status code: "
|
||||
<< static_cast<int>(response.getHTTPStatusCode())
|
||||
<< ", body: "
|
||||
<< response.getBody();
|
||||
|
||||
if (!isSuccessfulResponse(response)) {
|
||||
return genError("Upload failed: HTTP "
|
||||
+ to_string(static_cast<int>(response.getHTTPStatusCode()))
|
||||
+ " - "
|
||||
+ response.getBody());
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#ifndef __FOG_CONNECTION_H__
|
||||
#define __FOG_CONNECTION_H__
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include "services_sdk/interfaces/i_http_client.h"
|
||||
#include "req_res_objects.h"
|
||||
#include "maybe_res.h"
|
||||
|
||||
class FogConnection
|
||||
{
|
||||
public:
|
||||
FogConnection(const std::string& token, const std::string& fog_address);
|
||||
|
||||
void setProxy(const std::string& hosts);
|
||||
Maybe<void> getCredentials();
|
||||
Maybe<void> getJWT();
|
||||
Maybe<void> uploadNginxConfig(const std::string& config_file_path);
|
||||
|
||||
private:
|
||||
std::string var_token;
|
||||
std::string var_fog;
|
||||
std::string agent_id;
|
||||
std::string tenant_id;
|
||||
std::string profile_id;
|
||||
std::string ra_token;
|
||||
std::string clientId;
|
||||
std::string clientSecret;
|
||||
std::unique_ptr<I_HttpClient> curl_client;
|
||||
};
|
||||
|
||||
#endif // __FOG_CONNECTION_H__
|
||||
@@ -11,8 +11,12 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <cstddef>
|
||||
#include <iostream>
|
||||
#include <unistd.h>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <getopt.h>
|
||||
|
||||
#include "agent_core_utilities.h"
|
||||
#include "debug.h"
|
||||
@@ -20,6 +24,7 @@
|
||||
#include "mainloop.h"
|
||||
#include "nginx_utils.h"
|
||||
#include "time_proxy.h"
|
||||
#include "fog_connection.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
@@ -43,6 +48,7 @@ public:
|
||||
environment.fini();
|
||||
time_proxy.fini();
|
||||
}
|
||||
|
||||
private:
|
||||
ShellCmd shell_cmd;
|
||||
MainloopComponent mainloop;
|
||||
@@ -63,12 +69,17 @@ printVersion()
|
||||
void
|
||||
printUsage(const char *prog_name)
|
||||
{
|
||||
cout << "Usage: " << prog_name << " [-v] [-i /path/to/nginx.conf] [-o /path/to/output.conf]" << '\n';
|
||||
cout << "Usage: " << prog_name << " [-v] [-i /path/to/nginx.conf] [-o /path/to/output.conf]" <<
|
||||
" [--upload --token <token> [--fog <address>]]" << '\n';
|
||||
cout << " -V Print version" << '\n';
|
||||
cout << " -v Enable verbose output" << '\n';
|
||||
cout << " -i input_file Specify input file (default is /etc/nginx/nginx.conf)" << '\n';
|
||||
cout << " -o output_file Specify output file (default is ./full_nginx.conf)" << '\n';
|
||||
cout << " -h Print this help message" << '\n';
|
||||
cout << " --upload Upload configuration to FOG (requires --token)" << '\n';
|
||||
cout << " --token <token> profile token for FOG upload" << '\n';
|
||||
cout << " --fog <address> FOG server address (default: inext-agents.cloud.ngen.checkpoint.com)" << '\n';
|
||||
cout << " --proxy <address> Proxy server to send the request through" << '\n';
|
||||
}
|
||||
|
||||
int
|
||||
@@ -76,9 +87,21 @@ main(int argc, char *argv[])
|
||||
{
|
||||
string nginx_input_file = "/etc/nginx/nginx.conf";
|
||||
string nginx_output_file = "full_nginx.conf";
|
||||
|
||||
string fog_address = "inext-agents.cloud.ngen.checkpoint.com";
|
||||
string token;
|
||||
string proxy_host;
|
||||
bool upload_flag = false;
|
||||
int opt;
|
||||
while ((opt = getopt(argc, argv, "Vvhi:o:h")) != -1) {
|
||||
|
||||
static struct option long_options[] = {
|
||||
{"upload", no_argument, 0, 'u'},
|
||||
{"token", required_argument, 0, 1001},
|
||||
{"fog", required_argument, 0, 1002},
|
||||
{"proxy", required_argument, 0, 1003},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
while ((opt = getopt_long(argc, argv, "Vvhi:o:", long_options, nullptr)) != -1) {
|
||||
switch (opt) {
|
||||
case 'V':
|
||||
printVersion();
|
||||
@@ -95,18 +118,36 @@ main(int argc, char *argv[])
|
||||
case 'h':
|
||||
printUsage(argv[0]);
|
||||
return 0;
|
||||
case 'u':
|
||||
upload_flag = true;
|
||||
break;
|
||||
case 1001: // --token
|
||||
token = optarg;
|
||||
break;
|
||||
case 1002: // --fog
|
||||
fog_address = optarg;
|
||||
break;
|
||||
case 1003: // --proxy
|
||||
proxy_host = optarg;
|
||||
break;
|
||||
default:
|
||||
printUsage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = optind; i < argc;) {
|
||||
for (int i = optind; i < argc; ++i) {
|
||||
cerr << "Unknown argument: " << argv[i] << '\n';
|
||||
printUsage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (upload_flag && token.empty()) {
|
||||
cerr << "Error: --upload requires --token to be specified" << '\n';
|
||||
printUsage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
dbgTrace(D_NGINX_MANAGER) << "Starting nginx configuration collector";
|
||||
|
||||
MainComponent main_component;
|
||||
@@ -144,5 +185,43 @@ main(int argc, char *argv[])
|
||||
|
||||
cout << "Full nginx configuration file was successfully generated: " << result.unpack() << '\n';
|
||||
|
||||
if (upload_flag) {
|
||||
cout << "Uploading configuration to FOG server: " << fog_address << '\n';
|
||||
|
||||
string full_fog_url = fog_address;
|
||||
if (fog_address.find("http://") != 0 && fog_address.find("https://") != 0) {
|
||||
full_fog_url = "https://" + fog_address;
|
||||
}
|
||||
|
||||
FogConnection fog_connection(token, full_fog_url);
|
||||
|
||||
if (!proxy_host.empty()) {
|
||||
fog_connection.setProxy(proxy_host);
|
||||
}
|
||||
|
||||
auto credentials_result = fog_connection.getCredentials();
|
||||
if (!credentials_result.ok()) {
|
||||
cerr
|
||||
<< "Failed to register agent with the FOG. with error: "
|
||||
<< credentials_result.getErr()
|
||||
<< '\n';
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto jwt_result = fog_connection.getJWT();
|
||||
if (!jwt_result.ok()) {
|
||||
cerr << "Failed to get JWT token. with error:" << jwt_result.getErr() << '\n';
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto upload_result = fog_connection.uploadNginxConfig(result.unpack());
|
||||
if (!upload_result.ok()) {
|
||||
cerr << "Failed to upload nginx config file to FOG. with error:" << upload_result.getErr() << '\n';
|
||||
return 1;
|
||||
}
|
||||
|
||||
cout << "Successfully uploaded configuration to FOG server." << '\n';
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
#ifndef __REQ_RES_OBJECTS_H__
|
||||
#define __REQ_RES_OBJECTS_H__
|
||||
|
||||
#include "cereal/archives/json.hpp"
|
||||
#include "cereal/types/string.hpp"
|
||||
#include "cereal/types/vector.hpp"
|
||||
#include "cereal/types/map.hpp"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
USE_DEBUG_FLAG(D_NGINX_MANAGER);
|
||||
|
||||
struct AgentRegistrationRequest
|
||||
{
|
||||
struct AuthData
|
||||
{
|
||||
template<class Archive>
|
||||
void serialize(Archive& ar)
|
||||
{
|
||||
try {
|
||||
ar(cereal::make_nvp("authenticationMethod", authenticationMethod));
|
||||
ar(cereal::make_nvp("data", data));
|
||||
} catch (const cereal::Exception &e) {
|
||||
dbgWarning(D_NGINX_MANAGER) << "Serialization error in AuthData: " << e.what();
|
||||
ar.setNextName(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
std::string authenticationMethod;
|
||||
std::string data;
|
||||
};
|
||||
|
||||
struct MetaData
|
||||
{
|
||||
template<class Archive>
|
||||
void serialize(Archive& ar)
|
||||
{
|
||||
try {
|
||||
ar(cereal::make_nvp("agentName", agentName));
|
||||
ar(cereal::make_nvp("agentType", agentType));
|
||||
ar(cereal::make_nvp("platform", platform));
|
||||
ar(cereal::make_nvp("architecture", architecture));
|
||||
for (const auto& pair : additionalMetaData) {
|
||||
ar(cereal::make_nvp(pair.first.c_str(), pair.second));
|
||||
}
|
||||
} catch (const cereal::Exception &e) {
|
||||
dbgWarning(D_NGINX_MANAGER) << "Serialization error in MetaData: " << e.what();
|
||||
ar.setNextName(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
std::string agentName;
|
||||
std::string agentType;
|
||||
std::string platform;
|
||||
std::string architecture;
|
||||
std::map<std::string, std::string> additionalMetaData;
|
||||
};
|
||||
|
||||
template<class Archive>
|
||||
void serialize(Archive& ar)
|
||||
{
|
||||
try {
|
||||
ar(cereal::make_nvp("authenticationData", authenticationData));
|
||||
ar(cereal::make_nvp("metaData", metaData));
|
||||
} catch (const cereal::Exception &e) {
|
||||
dbgWarning(D_NGINX_MANAGER) << "Serialization error in AgentRegistrationRequest: " << e.what();
|
||||
ar.setNextName(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<AuthData> authenticationData;
|
||||
MetaData metaData;
|
||||
};
|
||||
|
||||
struct TokenRequest
|
||||
{
|
||||
template<class Archive>
|
||||
void serialize(Archive& ar)
|
||||
{
|
||||
try {
|
||||
ar(cereal::make_nvp("login", login));
|
||||
ar(cereal::make_nvp("password", password));
|
||||
} catch (const cereal::Exception &e) {
|
||||
dbgWarning(D_NGINX_MANAGER) << "Serialization error in TokenRequest: " << e.what();
|
||||
ar.setNextName(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
std::string login;
|
||||
std::string password;
|
||||
};
|
||||
|
||||
struct AgentRegistrationResponse
|
||||
{
|
||||
template<class Archive>
|
||||
void serialize(Archive& ar)
|
||||
{
|
||||
try {
|
||||
ar(cereal::make_nvp("agentId", agentId));
|
||||
ar(cereal::make_nvp("clientId", clientId));
|
||||
ar(cereal::make_nvp("clientSecret", clientSecret));
|
||||
ar(cereal::make_nvp("tenantId", tenantId));
|
||||
ar(cereal::make_nvp("profileId", profileId));
|
||||
} catch (const cereal::Exception &e) {
|
||||
dbgWarning(D_NGINX_MANAGER) << "Serialization error in AgentRegistrationResponse: " << e.what();
|
||||
ar.setNextName(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
std::string agentId;
|
||||
std::string clientId;
|
||||
std::string clientSecret;
|
||||
std::string tenantId;
|
||||
std::string profileId;
|
||||
};
|
||||
|
||||
struct TokenResponse
|
||||
{
|
||||
template<class Archive>
|
||||
void serialize(Archive& ar)
|
||||
{
|
||||
try {
|
||||
ar(cereal::make_nvp("access_token", access_token));
|
||||
} catch (const cereal::Exception &e) {
|
||||
dbgWarning(D_NGINX_MANAGER) << "Serialization error in TokenResponse: " << e.what();
|
||||
ar.setNextName(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
std::string access_token;
|
||||
};
|
||||
#endif
|
||||
Reference in New Issue
Block a user