* Add base PROXY support implementation * Remove some dynamic memory allocations * Rewrite configuration reading module * Make everything in core connector memory resource aware * Add logs to check is proxy is used * Add a proxy fallback, and read proxy from env * Add config entry to check application * Cleanup includes * Ddd configuration dump to check application * Update rhel8 packages * Fix http headers bug when using proxy server * Fix formatting * Fix bad optional access * Fix configuration check regresion * Fix memory management issue, remove strict allocators and make connector more polite to memory overflow errors * Fix initialization of core handler
283 lines
9.8 KiB
C++
283 lines
9.8 KiB
C++
#pragma once
|
|
|
|
#include "rublon/configuration.hpp"
|
|
#include "rublon/session.hpp"
|
|
#include <cstdio>
|
|
#include <rublon/bits.hpp>
|
|
#include <rublon/core_handler.hpp>
|
|
#include <rublon/curl.hpp>
|
|
#include <rublon/error.hpp>
|
|
#include <rublon/json.hpp>
|
|
#include <rublon/memory.hpp>
|
|
#include <rublon/utils.hpp>
|
|
|
|
#include <rapidjson/prettywriter.h>
|
|
#include <rapidjson/rapidjson.h>
|
|
#include <string>
|
|
#include <string_view>
|
|
|
|
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, closefile_deleter > pipe(popen(cmd, "r"));
|
|
if(!pipe) {
|
|
return "";
|
|
}
|
|
while(fgets(buffer.data(), static_cast< int >(buffer.size()), pipe.get()) != nullptr) {
|
|
result += buffer.data();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
std::map< std::string, std::string > getSSHDConfig() {
|
|
std::istringstream iss(exec("sshd -T"));
|
|
std::map< std::string, std::string > result;
|
|
for(std::string line; std::getline(iss, line);) {
|
|
auto first_token = line.substr(0, line.find(' '));
|
|
result[first_token] = line.substr(line.find(' ') + 1);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
class Status {
|
|
std::string_view _statusDirPath = "/var/lib/rublon";
|
|
std::string_view _statusFilePath = "/var/lib/rublon/install.json";
|
|
|
|
RapidJSONPMRStackAlloc< 4 * 1024 > _alloc;
|
|
Document _data;
|
|
bool _statusUpdated;
|
|
|
|
std::string_view _appVersionKey = "/appVer";
|
|
std::string_view _appTypeKey = "/type";
|
|
std::string_view _paramSystemName = "/params/os";
|
|
std::string_view _paramSSHDBase = "/params/sshd_config/";
|
|
std::string_view _configBase = "/config/";
|
|
|
|
public:
|
|
Status() : _alloc{}, _data{&_alloc}, _statusUpdated{false} {
|
|
if(not details::exists(_statusFilePath.data())) {
|
|
log(LogLevel::Info, "application first run, creating status file at %s", _statusFilePath.data());
|
|
details::mkdir(_statusDirPath.data());
|
|
details::touch(_statusFilePath.data());
|
|
}
|
|
|
|
std::ifstream ifs{_statusFilePath.data()};
|
|
if(!ifs.is_open()) {
|
|
return;
|
|
}
|
|
|
|
rapidjson::IStreamWrapper isw{ifs};
|
|
_data.ParseStream(isw);
|
|
}
|
|
|
|
void updateAppVersion(std::string_view newVersion) {
|
|
RapidJSONPMRStackAlloc< 512 > stackAlloc;
|
|
auto jsonPointer = JSONPointer{_appVersionKey.data(), &stackAlloc};
|
|
auto version = jsonPointer.Get(_data);
|
|
if(not version || version->GetString() != newVersion) {
|
|
markUpdated();
|
|
jsonPointer.Set(_data, Value{newVersion.data(), _data.GetAllocator()});
|
|
}
|
|
}
|
|
|
|
void updateSystemVersion(std::string_view system) {
|
|
RapidJSONPMRStackAlloc< 512 > stackAlloc;
|
|
auto jsonPointer = JSONPointer{_paramSystemName.data(), &stackAlloc};
|
|
auto version = jsonPointer.Get(_data);
|
|
if(not version || version->GetString() != system) {
|
|
markUpdated();
|
|
jsonPointer.Set(_data, Value{system.data(), _data.GetAllocator()});
|
|
}
|
|
}
|
|
|
|
void updateSSHDConfig() {
|
|
using namespace std::string_view_literals;
|
|
constexpr auto keys = make_array< std::string_view >("authenticationmethods"sv,
|
|
"challengeresponseauthentication"sv,
|
|
"kbdinteractiveauthentication"sv,
|
|
"logingracetime"sv,
|
|
"maxauthtries"sv,
|
|
"passwordauthentication"sv,
|
|
"permitemptypasswords"sv,
|
|
"permitrootlogin"sv,
|
|
"pubkeyauthentication"sv,
|
|
"usepam"sv);
|
|
|
|
auto config = getSSHDConfig();
|
|
|
|
for(const auto key : keys) {
|
|
auto [currentPair, inserted] = config.try_emplace(std::string{key.data()}, "N/A");
|
|
auto & [currentKey, currentValue] = *currentPair;
|
|
|
|
const auto jsonPath = std::string{_paramSSHDBase.data()} + key.data();
|
|
|
|
RapidJSONPMRStackAlloc< 512 > stackAlloc;
|
|
auto jsonPointer = JSONPointer{jsonPath.c_str(), &stackAlloc};
|
|
|
|
auto oldValue = jsonPointer.Get(_data);
|
|
|
|
if(not oldValue || oldValue->GetString() != currentValue) {
|
|
_statusUpdated = true;
|
|
jsonPointer.Set(_data, Value{currentValue.c_str(), _data.GetAllocator()});
|
|
}
|
|
}
|
|
}
|
|
|
|
template < typename T >
|
|
void updateRublonConfigParameter(std::string_view name, const T & newParam) {
|
|
memory::MonotonicStack_1k_Resource memoryResource{};
|
|
RapidJSONPMRAlloc stackAlloc{&memoryResource};
|
|
|
|
std::pmr::string jsonPath{&memoryResource};
|
|
jsonPath += _configBase;
|
|
jsonPath += name;
|
|
|
|
JSONPointer jsonPointer{jsonPath.c_str(), &stackAlloc};
|
|
auto * param = jsonPointer.Get(_data);
|
|
|
|
Document::AllocatorType & alloc = _data.GetAllocator();
|
|
Value newValue;
|
|
|
|
if constexpr(std::is_same_v< T, std::pmr::string > || std::is_same_v< T, std::string_view >) {
|
|
newValue.SetString(newParam.data(), static_cast< rapidjson::SizeType >(newParam.size()), alloc);
|
|
if(!param || !param->IsString() || param->GetString() != newParam) {
|
|
markUpdated();
|
|
jsonPointer.Set(_data, newValue);
|
|
}
|
|
} else if constexpr(std::is_same_v< T, bool >) {
|
|
newValue.SetBool(newParam);
|
|
if(!param || !param->IsBool() || param->GetBool() != newParam) {
|
|
markUpdated();
|
|
jsonPointer.Set(_data, newValue);
|
|
}
|
|
} else if constexpr(std::is_integral_v< T >) {
|
|
newValue.SetInt(static_cast< int >(newParam));
|
|
if(!param || !param->IsInt() || param->GetInt() != static_cast< int >(newParam)) {
|
|
markUpdated();
|
|
jsonPointer.Set(_data, newValue);
|
|
}
|
|
}
|
|
}
|
|
|
|
// For std::optional<T>
|
|
template < typename T >
|
|
void updateRublonConfigParameter(std::string_view name, const std::optional< T > & optParam) {
|
|
memory::MonotonicStack_1k_Resource memoryResource{};
|
|
RapidJSONPMRAlloc stackAlloc{&memoryResource};
|
|
|
|
std::pmr::string fullPath{&memoryResource};
|
|
fullPath += _configBase;
|
|
fullPath += name;
|
|
|
|
JSONPointer jsonPointer{fullPath.c_str(), &stackAlloc};
|
|
Value* existing = jsonPointer.Get(_data);
|
|
|
|
if (optParam.has_value()) {
|
|
// Delegate to regular (non-optional) setter
|
|
updateRublonConfigParameter(name, *optParam);
|
|
} else {
|
|
// Set the entire field to `null` if no value
|
|
if (!existing || !existing->IsNull()) {
|
|
markUpdated();
|
|
Value nullValue(rapidjson::kNullType);
|
|
jsonPointer.Set(_data, nullValue);
|
|
}
|
|
}
|
|
}
|
|
|
|
void updateRublonConfig(const Configuration & config) {
|
|
updateRublonConfigParameter("prompt", config.prompt);
|
|
updateRublonConfigParameter("logging", config.logging);
|
|
updateRublonConfigParameter("autopushPrompt", config.autopushPrompt);
|
|
updateRublonConfigParameter("failMode", static_cast<int>(config.failMode));
|
|
updateRublonConfigParameter("nonInteractiveMode", config.nonInteractiveMode);
|
|
|
|
updateRublonConfigParameter("proxyType", config.proxyType);
|
|
updateRublonConfigParameter("proxyHost", config.proxyHost);
|
|
updateRublonConfigParameter("proxyUsername", config.proxyUsername);
|
|
updateRublonConfigParameter("proxyPassword", config.proxyPass);
|
|
updateRublonConfigParameter("proxyPort", config.proxyPort);
|
|
|
|
updateRublonConfigParameter("proxyAuthRequired", config.proxyAuthRequired);
|
|
updateRublonConfigParameter("proxyEnabled", config.proxyEnabled);
|
|
}
|
|
|
|
void markUpdated() {
|
|
_statusUpdated = true;
|
|
}
|
|
|
|
bool updated() const {
|
|
return _statusUpdated;
|
|
}
|
|
|
|
void save() {
|
|
if(updated()) {
|
|
memory::Monotonic_8k_Resource tmpResource;
|
|
RapidJSONPMRAlloc alloc{&tmpResource};
|
|
FileWriter s{_statusFilePath};
|
|
rapidjson::PrettyWriter< FileWriter, rapidjson::UTF8<>, rapidjson::UTF8<>, RapidJSONPMRAlloc > writer{s, &alloc};
|
|
writer.SetIndent(' ', 2);
|
|
_data.Accept(writer);
|
|
}
|
|
}
|
|
|
|
std::string print() {
|
|
std::string result;
|
|
memory::Monotonic_8k_Resource tmpResource;
|
|
RapidJSONPMRAlloc alloc{&tmpResource};
|
|
StringWriter s{result};
|
|
rapidjson::PrettyWriter< StringWriter< std::string >, rapidjson::UTF8<>, rapidjson::UTF8<>, RapidJSONPMRAlloc > writer{s, &alloc};
|
|
writer.SetIndent(' ', 2);
|
|
_data.Accept(writer);
|
|
|
|
return result;
|
|
}
|
|
|
|
Document & data() {
|
|
return _data;
|
|
}
|
|
};
|
|
|
|
class CheckApplication {
|
|
const Session & _sesion;
|
|
tl::expected< bool, Error > persistStatus(Status & status) const {
|
|
status.data().RemoveMember("systemToken");
|
|
status.save();
|
|
return true;
|
|
}
|
|
|
|
public:
|
|
CheckApplication(const Session & session) : _sesion{session} {}
|
|
|
|
tl::expected< int, Error > call(const CoreHandler_t & coreHandler, std::string_view systemToken) const {
|
|
memory::MonotonicStack_2k_Resource mr;
|
|
RapidJSONPMRAlloc alloc{&mr};
|
|
constexpr std::string_view api = "/api/app/init";
|
|
Status status;
|
|
|
|
const auto persist = [&](const auto /*ok*/) { return this->persistStatus(status); };
|
|
|
|
status.updateAppVersion(RUBLON_VERSION_STRING);
|
|
status.updateSystemVersion(details::osName(&mr));
|
|
status.updateSSHDConfig();
|
|
status.updateRublonConfig(_sesion.config());
|
|
|
|
if(status.updated()) {
|
|
auto & alloc = status.data().GetAllocator();
|
|
status.data().AddMember("systemToken", Value{systemToken.data(), alloc}, alloc);
|
|
return coreHandler.request(alloc, api, status.data()).and_then(persist);
|
|
}
|
|
return 0;
|
|
}
|
|
};
|
|
|
|
} // namespace rublon
|