From cd65d742a5ae800440f480b685544b97010e5287 Mon Sep 17 00:00:00 2001 From: Bartosz Wieczorek Date: Fri, 30 May 2025 10:05:02 +0200 Subject: [PATCH] Add base PROXY support implementation --- PAM/ssh/include/rublon/check_application.hpp | 9 +++- PAM/ssh/include/rublon/configuration.hpp | 22 +++++++-- PAM/ssh/include/rublon/core_handler.hpp | 2 +- PAM/ssh/include/rublon/curl.hpp | 49 ++++++++++++++++++-- PAM/ssh/include/rublon/memory.hpp | 11 +++++ PAM/ssh/include/rublon/static_string.hpp | 6 +++ PAM/ssh/include/rublon/websockets.hpp | 34 +++++++++++++- 7 files changed, 121 insertions(+), 12 deletions(-) diff --git a/PAM/ssh/include/rublon/check_application.hpp b/PAM/ssh/include/rublon/check_application.hpp index c038c75..b84a595 100644 --- a/PAM/ssh/include/rublon/check_application.hpp +++ b/PAM/ssh/include/rublon/check_application.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -13,10 +14,16 @@ namespace rublon { +struct closefile_deleter { + void operator()(FILE * f) const { + pclose(f); + } +}; + std::string exec(const char * cmd) { std::array< char, 128 > buffer; 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) { return ""; } diff --git a/PAM/ssh/include/rublon/configuration.hpp b/PAM/ssh/include/rublon/configuration.hpp index 6e8ae4a..5c335d0 100644 --- a/PAM/ssh/include/rublon/configuration.hpp +++ b/PAM/ssh/include/rublon/configuration.hpp @@ -28,6 +28,14 @@ class Configuration { bool autopushPrompt{}; FailMode failMode{}; bool nonInteractiveMode{}; + + StaticString< 8 > proxyType{}; + StaticString< 4096 > proxyServer{}; + StaticString< 256 > proxyUsername{}; + StaticString< 256 > proxyPass{}; + int proxyPort{}; + bool proxyAuthRequired{}; + bool proxyEnabled{}; }; namespace { @@ -139,7 +147,7 @@ constexpr auto make_entry(const char * name, const char * defaultValue) { 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::systemToken >("systemToken", 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::autopushPrompt >("autopushPrompt", "false"), 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 { @@ -156,7 +172,7 @@ class ConfigurationFactory { ConfigurationFactory() = default; std::optional< Configuration > systemConfig() { - memory::MonotonicStackResource< 8 * 1024 > stackResource; + memory::MonotonicStackResource< 16 * 1024 > stackResource; Configuration configuration{}; std::ifstream file(std::filesystem::path{"/etc/rublon.config"}); diff --git a/PAM/ssh/include/rublon/core_handler.hpp b/PAM/ssh/include/rublon/core_handler.hpp index 0168318..1e88c78 100755 --- a/PAM/ssh/include/rublon/core_handler.hpp +++ b/PAM/ssh/include/rublon/core_handler.hpp @@ -73,7 +73,7 @@ class CoreHandler : public CoreHandlerInterface< CoreHandler< HttpHandler > > { public: CoreHandler() = delete; - CoreHandler(const Configuration & config) : _config{config}, _ws{std::make_unique(_config.apiServer)}, http{} { + CoreHandler(const Configuration & config) : _config{config}, _ws{std::make_unique(_config.apiServer)}, http{_config} { log(LogLevel::Debug, "Core Handler apiServer: %s", _config.apiServer.c_str()); } CoreHandler(CoreHandler &&) noexcept = default; diff --git a/PAM/ssh/include/rublon/curl.hpp b/PAM/ssh/include/rublon/curl.hpp index 162962c..912aa91 100644 --- a/PAM/ssh/include/rublon/curl.hpp +++ b/PAM/ssh/include/rublon/curl.hpp @@ -1,7 +1,9 @@ #pragma once +#include "rublon/memory.hpp" #include #include +#include #include @@ -51,22 +53,59 @@ struct Response { class CURL { std::unique_ptr< ::CURL, void (*)(::CURL *) > curl; - + const Configuration &_config; 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 > 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}; - response_data.reserve(3000); + std::pmr::string response_data{&memoryResource}; + response_data.reserve(4_kB); 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) { 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())); }); + + // 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_URL, uri.data()); diff --git a/PAM/ssh/include/rublon/memory.hpp b/PAM/ssh/include/rublon/memory.hpp index 5a8c878..d6a9e7b 100644 --- a/PAM/ssh/include/rublon/memory.hpp +++ b/PAM/ssh/include/rublon/memory.hpp @@ -4,6 +4,13 @@ namespace rublon { namespace memory { + + namespace literals{ + constexpr std::uint64_t operator"" _kB(unsigned long long kilobytes) { + return kilobytes * 1024ULL; + } + } + struct default_memory_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_4k_HeapResource = StrictMonotonicHeapResource< 4 * 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_2k_HeapResource = MonotonicHeapResource< 2 * 1024 >; using Monotonic_4k_HeapResource = MonotonicHeapResource< 4 * 1024 >; using Monotonic_8k_HeapResource = MonotonicHeapResource< 8 * 1024 >; + using Monotonic_16k_HeapResource = MonotonicHeapResource< 4 * 1024 >; + using Monotonic_32k_HeapResource = MonotonicHeapResource< 8 * 1024 >; } // namespace memory // class RublonMemory { diff --git a/PAM/ssh/include/rublon/static_string.hpp b/PAM/ssh/include/rublon/static_string.hpp index c4f5298..5d0ac45 100644 --- a/PAM/ssh/include/rublon/static_string.hpp +++ b/PAM/ssh/include/rublon/static_string.hpp @@ -89,4 +89,10 @@ class StaticString : public details::StaticStringBase { private: std::array< char, N + 1 > m_str{}; }; + +template +bool operator==(const StaticString &lhs, const char* rhs){ + return strcmp(lhs.c_str(), rhs) == 0; +} + } // namespace rublon diff --git a/PAM/ssh/include/rublon/websockets.hpp b/PAM/ssh/include/rublon/websockets.hpp index e37c877..5abc469 100644 --- a/PAM/ssh/include/rublon/websockets.hpp +++ b/PAM/ssh/include/rublon/websockets.hpp @@ -1,5 +1,6 @@ #pragma once +#include "rublon/configuration.hpp" #include "rublon/json.hpp" #include "rublon/memory.hpp" #include "rublon/static_string.hpp" @@ -7,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -28,7 +30,7 @@ struct RublonEventData { }; class WebSocket { - std::string url; /// TODO pmr + std::reference_wrapper< const Configuration > _config; std::string_view urlv; bool event_received = false; @@ -42,7 +44,11 @@ class WebSocket { RublonEventData * currentEvent{nullptr}; 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) { LogLevel rlevel{}; if(level == LLL_ERR) @@ -70,6 +76,30 @@ class WebSocket { 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://"; if(urlv.substr(0, prefix.size()) == prefix)