From 03a03a17dee31197b5d938a1d06aa9745cb58192 Mon Sep 17 00:00:00 2001 From: Richard Chapman Date: Wed, 25 Sep 2024 16:49:12 +0100 Subject: [PATCH] WIP Toposerver secure support I think that the code at the server end should look SOMETHING like this Signed-off-by: Richard Chapman --- roxie/topo/CMakeLists.txt | 3 +++ roxie/topo/toposerver.cpp | 51 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/roxie/topo/CMakeLists.txt b/roxie/topo/CMakeLists.txt index a5339d81aca..0a370980992 100644 --- a/roxie/topo/CMakeLists.txt +++ b/roxie/topo/CMakeLists.txt @@ -34,6 +34,8 @@ include_directories ( . ${HPCC_SOURCE_DIR}/system/jlib ${HPCC_SOURCE_DIR}/system/include + ${HPCC_SOURCE_DIR}/roxie/roxie + ${HPCC_SOURCE_DIR}/system/security/securesocket ) if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG) @@ -54,5 +56,6 @@ HPCC_ADD_EXECUTABLE ( toposerver ${SRCS} ) install ( TARGETS toposerver RUNTIME DESTINATION ${EXEC_DIR} ) target_link_libraries ( toposerver + securesocket jlib ) diff --git a/roxie/topo/toposerver.cpp b/roxie/topo/toposerver.cpp index c5c184284b9..50a04662843 100644 --- a/roxie/topo/toposerver.cpp +++ b/roxie/topo/toposerver.cpp @@ -22,6 +22,7 @@ #include #include #include "portlist.h" +#include "roxie.hpp" #include "jlib.hpp" #include "jmisc.hpp" #include "jexcept.hpp" @@ -29,6 +30,8 @@ #include "jmd5.hpp" #include "jfile.hpp" #include "jptree.hpp" +#include "jsecrets.hpp" +#include "securesocket.hpp" /** * While billed as a topology server (and used for that by Roxie), this service actually remembers and @@ -84,6 +87,9 @@ unsigned timeoutHeartbeatAgent = 20000; // How long before an agent is marke unsigned removeHeartbeatInterval = 120000; // How long before a node is removed from list unsigned topologyReportInterval = 60000; // How often topology is reported to logging (if traceLevel >= 2) bool aborted = false; +bool tls = false; +Owned tlsConfig; +Owned secureContext; Semaphore stopping; StringBuffer topologyFile; @@ -218,12 +224,33 @@ void doServer(ISocket *socket) try { Owned p = ISocket::connect(me); + if (tlsConfig) + { + Owned secureCtx = createSecureSocketContextSynced(tlsConfig, ClientSocket); + if (!secureCtx) + throw makeStringException(ROXIE_TLS_ERROR, "toposerver failed creating secure context for roxie control message"); + Owned ssock = secureCtx->createSecureSocket(p.getClear()); + if (!ssock) + throw makeStringException(ROXIE_TLS_ERROR, "toposerver failed creating secure socket for roxie control message"); + + int status = ssock->secure_connect(); + if (status < 0) + { + StringBuffer err; + err.append("toposerver failed to establish secure connection to "); + me.getEndpointHostText(err); + err.append(": returned ").append(status); + throw makeStringException(ROXIE_TLS_ERROR, err.str()); + } + p.setown(ssock.getClear()); + } // TLS TODO: secure_connect() here if globally configured for mtls ... p->write("\0\0\0\0", 4); p->close(); } catch (IException *e) { + EXCLOG(e); e->Release(); } } @@ -232,8 +259,19 @@ void doServer(ISocket *socket) { try { - Owned client = socket->accept(); - // TLS TODO: secure_accept() here if globally configured for mtls ... + Owned client; + client.setown(socket->accept()); + if (tls) + { + Owned ssocket = secureContext->createSecureSocket(socket); + int status = ssocket->secure_accept(traceLevel); + if (status < 0) + { + // secure_accept may also DBGLOG() errors ... + shutdownAndCloseNoThrow(ssocket); + continue; + } + } timeoutTopology(); unsigned packetLen; client->read(&packetLen, 4); @@ -322,6 +360,7 @@ static constexpr const char * defaultYaml = R"!!( port: 9004 stdlog: true traceLevel: 1 + tls: false logging: queueDrop: 32 )!!"; @@ -372,6 +411,7 @@ int main(int argc, const char *argv[]) timeoutHeartbeatServer = topology->getPropInt("@timeoutHeartbeatServer", heartbeatInterval*6); removeHeartbeatInterval = topology->getPropInt("@removeHeartbeatInterval", heartbeatInterval*10); topologyReportInterval = topology->getPropInt("@topologyReportInterval", topologyReportInterval); + tls = topology->getPropBool("@tls", false); #ifndef _CONTAINERIZED if (topology->getPropBool("@stdlog", traceLevel != 0)) @@ -406,9 +446,14 @@ int main(int argc, const char *argv[]) DBGLOG("removeHeartbeatInterval value too small - setting to %u", removeHeartbeatInterval); } Owned socket = ISocket::create(topoPort); + if (tls) + { + tlsConfig.setown(getIssuerTlsSyncedConfig("local", nullptr, false)); + // MORE - should this be recreated periodically for cert rotation? + secureContext.setown(createSecureSocketContextSynced(tlsConfig, ServerSocket)); + } if (traceLevel) DBGLOG("Topology server starting on port %u", topoPort); - writeSentinelFile(sentinelFile); doServer(socket); if (traceLevel)