Add base PROXY support implementation

This commit is contained in:
Bartosz Wieczorek 2025-05-30 10:05:02 +02:00
parent af64f8e9e3
commit cd65d742a5
7 changed files with 121 additions and 12 deletions

View File

@ -1,5 +1,6 @@
#pragma once #pragma once
#include <cstdio>
#include <rublon/bits.hpp> #include <rublon/bits.hpp>
#include <rublon/core_handler.hpp> #include <rublon/core_handler.hpp>
#include <rublon/curl.hpp> #include <rublon/curl.hpp>
@ -13,10 +14,16 @@
namespace rublon { namespace rublon {
struct closefile_deleter {
void operator()(FILE * f) const {
pclose(f);
}
};
std::string exec(const char * cmd) { std::string exec(const char * cmd) {
std::array< char, 128 > buffer; std::array< char, 128 > buffer;
std::string result; std::string result;
std::unique_ptr< FILE, decltype(&pclose) > pipe(popen(cmd, "r"), pclose); std::unique_ptr< FILE, closefile_deleter > pipe(popen(cmd, "r"));
if(!pipe) { if(!pipe) {
return ""; return "";
} }

View File

@ -28,6 +28,14 @@ class Configuration {
bool autopushPrompt{}; bool autopushPrompt{};
FailMode failMode{}; FailMode failMode{};
bool nonInteractiveMode{}; bool nonInteractiveMode{};
StaticString< 8 > proxyType{};
StaticString< 4096 > proxyServer{};
StaticString< 256 > proxyUsername{};
StaticString< 256 > proxyPass{};
int proxyPort{};
bool proxyAuthRequired{};
bool proxyEnabled{};
}; };
namespace { namespace {
@ -139,7 +147,7 @@ constexpr auto make_entry(const char * name, const char * defaultValue) {
return Entry{name, defaultValue, Entry::make_read_function< member >()}; return Entry{name, defaultValue, Entry::make_read_function< member >()};
} }
constexpr static inline std::array< Entry, 9 > configurationVariables = { // constexpr static inline std::array< Entry, 16 > configurationVariables = {
make_entry< &Configuration::logging >("logging", "true"), make_entry< &Configuration::logging >("logging", "true"),
make_entry< &Configuration::systemToken >("systemToken", nullptr), make_entry< &Configuration::systemToken >("systemToken", nullptr),
make_entry< &Configuration::secretKey >("secretKey", nullptr), make_entry< &Configuration::secretKey >("secretKey", nullptr),
@ -148,7 +156,15 @@ constexpr static inline std::array< Entry, 9 > configurationVariables = { //
make_entry< &Configuration::enablePasswdEmail >("enablePasswdEmail", "true"), make_entry< &Configuration::enablePasswdEmail >("enablePasswdEmail", "true"),
make_entry< &Configuration::autopushPrompt >("autopushPrompt", "false"), make_entry< &Configuration::autopushPrompt >("autopushPrompt", "false"),
make_entry< &Configuration::failMode >("failMode", "deny"), make_entry< &Configuration::failMode >("failMode", "deny"),
make_entry< &Configuration::nonInteractiveMode >("nonInteractiveMode", "false") make_entry< &Configuration::nonInteractiveMode >("nonInteractiveMode", "false"),
make_entry< &Configuration::proxyType >("proxyType", nullptr),
make_entry< &Configuration::proxyServer >("proxyServer", nullptr),
make_entry< &Configuration::proxyUsername >("proxyUsername", nullptr),
make_entry< &Configuration::proxyPass >("proxyPass", nullptr),
make_entry< &Configuration::proxyPort >("proxyPort", "8080"),
make_entry< &Configuration::proxyAuthRequired >("proxyAuthRequired", "false"),
make_entry< &Configuration::proxyEnabled >("proxyEnabled", "false") //
}; };
class ConfigurationFactory { class ConfigurationFactory {
@ -156,7 +172,7 @@ class ConfigurationFactory {
ConfigurationFactory() = default; ConfigurationFactory() = default;
std::optional< Configuration > systemConfig() { std::optional< Configuration > systemConfig() {
memory::MonotonicStackResource< 8 * 1024 > stackResource; memory::MonotonicStackResource< 16 * 1024 > stackResource;
Configuration configuration{}; Configuration configuration{};
std::ifstream file(std::filesystem::path{"/etc/rublon.config"}); std::ifstream file(std::filesystem::path{"/etc/rublon.config"});

View File

@ -73,7 +73,7 @@ class CoreHandler : public CoreHandlerInterface< CoreHandler< HttpHandler > > {
public: public:
CoreHandler() = delete; CoreHandler() = delete;
CoreHandler(const Configuration & config) : _config{config}, _ws{std::make_unique<WebSocket>(_config.apiServer)}, http{} { CoreHandler(const Configuration & config) : _config{config}, _ws{std::make_unique<WebSocket>(_config.apiServer)}, http{_config} {
log(LogLevel::Debug, "Core Handler apiServer: %s", _config.apiServer.c_str()); log(LogLevel::Debug, "Core Handler apiServer: %s", _config.apiServer.c_str());
} }
CoreHandler(CoreHandler &&) noexcept = default; CoreHandler(CoreHandler &&) noexcept = default;

View File

@ -1,7 +1,9 @@
#pragma once #pragma once
#include "rublon/memory.hpp"
#include <rublon/error.hpp> #include <rublon/error.hpp>
#include <rublon/utils.hpp> #include <rublon/utils.hpp>
#include <rublon/configuration.hpp>
#include <tl/expected.hpp> #include <tl/expected.hpp>
@ -51,22 +53,59 @@ struct Response {
class CURL { class CURL {
std::unique_ptr< ::CURL, void (*)(::CURL *) > curl; std::unique_ptr< ::CURL, void (*)(::CURL *) > curl;
const Configuration &_config;
public: public:
CURL() : curl{std::unique_ptr< ::CURL, void (*)(::CURL *) >(curl_easy_init(), curl_easy_cleanup)} {} CURL(const Configuration &config) : curl{std::unique_ptr< ::CURL, void (*)(::CURL *) >(curl_easy_init(), curl_easy_cleanup)}, _config{config} {}
tl::expected< std::reference_wrapper< Response >, ConnectionError > tl::expected< std::reference_wrapper< Response >, ConnectionError >
request(std::string_view uri, const Request & request, Response & response) const { request(std::string_view uri, const Request & request, Response & response) const {
memory::MonotonicStackResource< 4 * 1024 > stackResource; using namespace memory::literals;
memory::Monotonic_16k_HeapResource memoryResource;
std::pmr::string response_data{&stackResource}; std::pmr::string response_data{&memoryResource};
response_data.reserve(3000); response_data.reserve(4_kB);
auto curl_headers = std::unique_ptr< curl_slist, void (*)(curl_slist *) >(nullptr, curl_slist_free_all); auto curl_headers = std::unique_ptr< curl_slist, void (*)(curl_slist *) >(nullptr, curl_slist_free_all);
std::for_each(request.headers.begin(), request.headers.end(), [&](auto header) { std::for_each(request.headers.begin(), request.headers.end(), [&](auto header) {
log(LogLevel::Debug, "%s header: %s: %s", "CURL", header.first.c_str(), header.second.c_str()); log(LogLevel::Debug, "%s header: %s: %s", "CURL", header.first.c_str(), header.second.c_str());
curl_headers.reset(curl_slist_append(curl_headers.release(), (header.first + ": " + header.second).c_str())); curl_headers.reset(curl_slist_append(curl_headers.release(), (header.first + ": " + header.second).c_str()));
}); });
// Optional: Build full proxy URL if proxy is enabled
if (_config.proxyEnabled) {
std::pmr::string proxyUrl{&memoryResource};
proxyUrl.reserve(4_kB);
if (_config.proxyType == "http" || _config.proxyType == "https" || _config.proxyType == "socks4" || _config.proxyType == "socks5") {
proxyUrl = _config.proxyType.c_str();
proxyUrl += "://";
proxyUrl += _config.proxyServer.c_str();
if (_config.proxyPort > 0) {
proxyUrl += ":";
proxyUrl += std::to_string(_config.proxyPort);
}
curl_easy_setopt(curl.get(), CURLOPT_PROXY, proxyUrl.c_str());
if (_config.proxyType == "socks4") {
curl_easy_setopt(curl.get(), CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4);
} else if (_config.proxyType == "socks5") {
curl_easy_setopt(curl.get(), CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5);
} else {
curl_easy_setopt(curl.get(), CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
}
if (_config.proxyAuthRequired) {
std::pmr::string proxyAuth{&memoryResource};
proxyAuth.reserve(1_kB);
_config.proxyUsername.c_str();
proxyAuth += ":";
proxyAuth += _config.proxyPass.c_str();
curl_easy_setopt(curl.get(), CURLOPT_PROXYUSERPWD, proxyAuth.c_str());
}
}
}
curl_easy_setopt(curl.get(), CURLOPT_VERBOSE, 0); curl_easy_setopt(curl.get(), CURLOPT_VERBOSE, 0);
curl_easy_setopt(curl.get(), CURLOPT_URL, uri.data()); curl_easy_setopt(curl.get(), CURLOPT_URL, uri.data());

View File

@ -4,6 +4,13 @@
namespace rublon { namespace rublon {
namespace memory { namespace memory {
namespace literals{
constexpr std::uint64_t operator"" _kB(unsigned long long kilobytes) {
return kilobytes * 1024ULL;
}
}
struct default_memory_resource { struct default_memory_resource {
static inline std::pmr::memory_resource * _mr = std::pmr::get_default_resource(); static inline std::pmr::memory_resource * _mr = std::pmr::get_default_resource();
}; };
@ -58,11 +65,15 @@ namespace memory {
using StrictMonotonic_2k_HeapResource = StrictMonotonicHeapResource< 2 * 1024 >; using StrictMonotonic_2k_HeapResource = StrictMonotonicHeapResource< 2 * 1024 >;
using StrictMonotonic_4k_HeapResource = StrictMonotonicHeapResource< 4 * 1024 >; using StrictMonotonic_4k_HeapResource = StrictMonotonicHeapResource< 4 * 1024 >;
using StrictMonotonic_8k_HeapResource = StrictMonotonicHeapResource< 8 * 1024 >; using StrictMonotonic_8k_HeapResource = StrictMonotonicHeapResource< 8 * 1024 >;
using StrictMonotonic_16k_HeapResource = StrictMonotonicHeapResource< 16 * 1024 >;
using StrictMonotonic_32k_HeapResource = StrictMonotonicHeapResource< 32 * 1024 >;
using Monotonic_1k_HeapResource = MonotonicHeapResource< 1 * 1024 >; using Monotonic_1k_HeapResource = MonotonicHeapResource< 1 * 1024 >;
using Monotonic_2k_HeapResource = MonotonicHeapResource< 2 * 1024 >; using Monotonic_2k_HeapResource = MonotonicHeapResource< 2 * 1024 >;
using Monotonic_4k_HeapResource = MonotonicHeapResource< 4 * 1024 >; using Monotonic_4k_HeapResource = MonotonicHeapResource< 4 * 1024 >;
using Monotonic_8k_HeapResource = MonotonicHeapResource< 8 * 1024 >; using Monotonic_8k_HeapResource = MonotonicHeapResource< 8 * 1024 >;
using Monotonic_16k_HeapResource = MonotonicHeapResource< 4 * 1024 >;
using Monotonic_32k_HeapResource = MonotonicHeapResource< 8 * 1024 >;
} // namespace memory } // namespace memory
// class RublonMemory { // class RublonMemory {

View File

@ -89,4 +89,10 @@ class StaticString : public details::StaticStringBase {
private: private:
std::array< char, N + 1 > m_str{}; std::array< char, N + 1 > m_str{};
}; };
template<size_t N>
bool operator==(const StaticString<N> &lhs, const char* rhs){
return strcmp(lhs.c_str(), rhs) == 0;
}
} // namespace rublon } // namespace rublon

View File

@ -1,5 +1,6 @@
#pragma once #pragma once
#include "rublon/configuration.hpp"
#include "rublon/json.hpp" #include "rublon/json.hpp"
#include "rublon/memory.hpp" #include "rublon/memory.hpp"
#include "rublon/static_string.hpp" #include "rublon/static_string.hpp"
@ -7,6 +8,7 @@
#include <chrono> #include <chrono>
#include <cstdio> #include <cstdio>
#include <cstring> #include <cstring>
#include <functional>
#include <rublon/error.hpp> #include <rublon/error.hpp>
#include <rublon/pam_action.hpp> #include <rublon/pam_action.hpp>
#include <rublon/utils.hpp> #include <rublon/utils.hpp>
@ -28,7 +30,7 @@ struct RublonEventData {
}; };
class WebSocket { class WebSocket {
std::string url; /// TODO pmr std::reference_wrapper< const Configuration > _config;
std::string_view urlv; std::string_view urlv;
bool event_received = false; bool event_received = false;
@ -42,7 +44,11 @@ class WebSocket {
RublonEventData * currentEvent{nullptr}; RublonEventData * currentEvent{nullptr};
public: public:
WebSocket(std::string_view uri) : url{uri.data()}, urlv{url} { WebSocket(const Configuration & config) : _config{config}, urlv{_config.get().apiServer.c_str(), _config.get().apiServer.size()} {
using namespace memory::literals;
memory::Monotonic_8k_HeapResource memoryResource;
const auto & cfg = _config.get();
auto lws_log_emit = [](int level, const char * line) { auto lws_log_emit = [](int level, const char * line) {
LogLevel rlevel{}; LogLevel rlevel{};
if(level == LLL_ERR) if(level == LLL_ERR)
@ -70,6 +76,30 @@ class WebSocket {
context = lws_create_context(&info); context = lws_create_context(&info);
if(cfg.proxyEnabled && (cfg.proxyType == "http" || cfg.proxyType == "https")) {
std::pmr::string proxyUrl{&memoryResource};
proxyUrl.reserve(4_kB);
proxyUrl += cfg.proxyType.data();
proxyUrl += "://";
if(cfg.proxyAuthRequired) {
proxyUrl += cfg.proxyUsername.c_str();
proxyUrl += ":";
proxyUrl += cfg.proxyPass.c_str();
proxyUrl += "@";
}
proxyUrl += cfg.proxyServer.c_str();
if(cfg.proxyPort > 0) {
proxyUrl += ":";
proxyUrl += std::to_string(cfg.proxyPort);
}
// Set environment variable for libwebsockets to pick up
setenv((cfg.proxyType == "https" ? "https_proxy" : "http_proxy"), proxyUrl.c_str(), 1);
}
const std::string_view prefix = "https://"; const std::string_view prefix = "https://";
if(urlv.substr(0, prefix.size()) == prefix) if(urlv.substr(0, prefix.size()) == prefix)