Skip to content

Commit

Permalink
HPCC-30411 Add support for dynamically updating TLS config
Browse files Browse the repository at this point in the history
Signed-off-by: Gavin Halliday <[email protected]>
  • Loading branch information
ghalliday committed Oct 25, 2023
1 parent 5161525 commit ee85cf9
Show file tree
Hide file tree
Showing 23 changed files with 625 additions and 406 deletions.
5 changes: 4 additions & 1 deletion common/thorhelper/thorsoapcall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1242,7 +1242,10 @@ class CWSCHelper : implements IWSCHelper, public CInterface
if (!ownedSC)
{
if (clientCert != NULL)
ownedSC.setown(createSecureSocketContextEx(clientCert->certificate, clientCert->privateKey, clientCert->passphrase, ClientSocket));
{
Owned<IPropertyTree> config = createSecureSocketConfig(clientCert->certificate, clientCert->privateKey, clientCert->passphrase);
ownedSC.setown(createSecureSocketContextEx2(config, ClientSocket));
}
else if (clientCertIssuer.length())
ownedSC.setown(createSecureSocketContextSecret(clientCertIssuer.str(), ClientSocket));
else
Expand Down
2 changes: 1 addition & 1 deletion esp/bindings/http/client/httpclient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ IHttpClient* CHttpClientContext::createHttpClient(const char* proxy, const char*
if (xproc)
m_ssctx.setown(xproc(m_config.get(),ClientSocket));
else
throw MakeStringException(-1, "procedure createSecureSocketContext can't be loaded");
throw MakeStringException(-1, "procedure createSecureSocketContextEx2 can't be loaded");

}
if(m_ssctx.get() == NULL)
Expand Down
25 changes: 12 additions & 13 deletions esp/clients/wsdfuaccess/wsdfuaccess.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -534,7 +534,6 @@ StringBuffer &encodeDFUFileMeta(StringBuffer &metaInfoBlob, IPropertyTree *metaI
metaInfo->serialize(metaInfoBlob);
const char *keyPairName = metaInfo->queryProp("keyPairName"); // NB: in container mode, this is the name of the secret containing the cert.

const char *privateKeyFName = nullptr;
Owned<IPropertyTree> metaInfoEnvelope = createPTree();
#ifdef _CONTAINERIZED
/* Encode the public certificate in the request. NB: this is an approach used for JWT token delegation.
Expand All @@ -543,24 +542,24 @@ StringBuffer &encodeDFUFileMeta(StringBuffer &metaInfoBlob, IPropertyTree *metaI
* If the size of this initial request was ever a concern, we could consider other ways to ensure a one-off
* delivery of this esp public signing cert. to dafilesrv, e.g. by dafilesrv reaching out to esp to request it.
*/
Owned<IPropertyTree> info = getIssuerTlsServerConfig(keyPairName);
if (!info)
Owned<const ISyncedPropertyTree> config = getIssuerTlsSyncedConfig(keyPairName);
if (!config || !config->isValid())
throw makeStringExceptionV(-1, "encodeDFUFileMeta: No '%s' MTLS certificate detected.", keyPairName);
privateKeyFName = info->queryProp("privatekey");
if (isEmptyString(privateKeyFName))
throw makeStringException(-1, "encodeDFUFileMeta: MTLS - private path missing");
const char *certPath = info->queryProp("certificate");
verifyex(certPath);
StringBuffer certificate;
certificate.loadFile(certPath);
verifyex(certificate.length());

Owned<const IPropertyTree> info = config->getTree();
const char *privateKeyText = info->queryProp("privatekey");
if (isEmptyString(privateKeyText))
throw makeStringException(-1, "encodeDFUFileMeta: MTLS - private key missing");
const char *certificate = info->queryProp("certificate");
verifyex(certificate);
metaInfoEnvelope->setProp("certificate", certificate);
Owned<CLoadedKey> privateKey = loadPrivateKeyFromMemory(privateKeyText, nullptr);
#else
privateKeyFName = environment->getPrivateKeyPath(keyPairName);
const char *privateKeyFName = privateKeyFName = environment->getPrivateKeyPath(keyPairName);
if (isEmptyString(privateKeyFName))
throw makeStringExceptionV(-1, "Key name '%s' is not found in environment settings: /EnvSettings/Keys/KeyPair.", keyPairName);
#endif
Owned<CLoadedKey> privateKey = loadPrivateKeyFromFile(privateKeyFName, nullptr);
#endif
StringBuffer metaInfoSignature;
digiSign(metaInfoSignature, metaInfoBlob.length(), metaInfoBlob.bytes(), *privateKey);

Expand Down
6 changes: 2 additions & 4 deletions esp/services/ws_dfu/ws_dfuService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6113,8 +6113,7 @@ void CWsDfuEx::dFUFileAccessCommon(IEspContext &context, const CDfsLogicalFileNa
StringBuffer dafilesrvHost;
#ifdef _CONTAINERIZED
keyPairName.set("signing");
Owned<IPropertyTree> info = getIssuerTlsServerConfig(keyPairName);
if (!info)
if (!hasIssuerTlsConfig(keyPairName))
throw makeStringExceptionV(-1, "dFUFileAccessCommon: file signing certificate ('%s') not defined in configuration.", keyPairName.str());

auto externalService = k8s::getDafileServiceFromConfig("stream");
Expand Down Expand Up @@ -6490,8 +6489,7 @@ bool CWsDfuEx::onDFUFileCreateV2(IEspContext &context, IEspDFUFileCreateV2Reques

#ifdef _CONTAINERIZED
keyPairName.set("signing");
Owned<IPropertyTree> info = getIssuerTlsServerConfig(keyPairName);
if (!info)
if (!hasIssuerTlsConfig(keyPairName))
throw makeStringExceptionV(-1, "onDFUFileCreateV2: file signing certificate ('%s' ) not defined in configuration.", keyPairName.str());

const char *planeName = clusterName;
Expand Down
15 changes: 3 additions & 12 deletions esp/test/httptest/httptest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,10 +152,7 @@ HttpClient::HttpClient(int threads, int times, const char* host, int port, FILE*
if(use_ssl)
{
#ifdef _USE_OPENSSL
if(sslconfig != NULL)
m_ssctx.setown(createSecureSocketContextEx2(sslconfig, ClientSocket));
else
m_ssctx.setown(createSecureSocketContext(ClientSocket));
m_ssctx.setown(createSecureSocketContextEx2(sslconfig, ClientSocket));
#else
throw MakeStringException(-1, "HttpClient: failure to create SSL connection to host '%s': OpenSSL not enabled in build", host);
#endif
Expand Down Expand Up @@ -614,10 +611,7 @@ HttpServer::HttpServer(int port, const char* in, FILE* ofile, bool use_ssl, IPro
if(use_ssl)
{
#ifdef _USE_OPENSSL
if(sslconfig != NULL)
m_ssctx.setown(createSecureSocketContextEx2(sslconfig, ServerSocket));
else
m_ssctx.setown(createSecureSocketContext(ServerSocket));
m_ssctx.setown(createSecureSocketContextEx2(sslconfig, ServerSocket));
#else
throw MakeStringException(-1, "HttpServer: failure to create SSL socket - OpenSSL not enabled in build");
#endif
Expand Down Expand Up @@ -1180,10 +1174,7 @@ HttpProxy::HttpProxy(int localport, const char* host, int port, FILE* ofile, boo
if(use_ssl)
{
#if _USE_OPENSSL
if(sslconfig != NULL)
m_ssctx.setown(createSecureSocketContextEx2(sslconfig, ClientSocket));
else
m_ssctx.setown(createSecureSocketContext(ClientSocket));
m_ssctx.setown(createSecureSocketContextEx2(sslconfig, ClientSocket));
#else
throw MakeStringException(-1, "HttpProxy: failure to create SSL connection to host '%s': OpenSSL not enabled in build", host);
#endif
Expand Down
5 changes: 1 addition & 4 deletions esp/tools/soapplus/http.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -505,10 +505,7 @@ HttpClient::HttpClient(IProperties* globals, const char* url, const char* inname
if (cfg && *cfg)
cfgtree.setown(createPTreeFromXMLFile(cfg));
}
if (cfgtree)
m_ssctx.setown(createSecureSocketContextEx2(cfgtree, ClientSocket));
else
m_ssctx.setown(createSecureSocketContext(ClientSocket));
m_ssctx.setown(createSecureSocketContextEx2(cfgtree, ClientSocket));
}
#else
throw MakeStringException(-1, "HttpClient: failure to create SSL socket - OpenSSL not enabled in build");
Expand Down
5 changes: 1 addition & 4 deletions esp/tools/soapplus/httpproxy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -582,10 +582,7 @@ HttpProxy::HttpProxy(int localport, const char* host, int port, FILE* ofile, boo
if(use_ssl)
{
#ifdef _USE_OPENSSL
if(sslconfig != NULL)
m_ssctx.setown(createSecureSocketContextEx2(sslconfig, ClientSocket));
else
m_ssctx.setown(createSecureSocketContext(ClientSocket));
m_ssctx.setown(createSecureSocketContextEx2(sslconfig, ClientSocket));
#else
throw MakeStringException(-1, "HttpProxy: failure to create SSL socket - OpenSSL not enabled in build");
#endif
Expand Down
3 changes: 1 addition & 2 deletions fs/dafilesrv/dafilesrv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -395,8 +395,7 @@ int main(int argc, const char* argv[])
// Use the "public" certificate issuer, unless it's visibility is "cluster" (meaning internal only)
const char *visibility = getComponentConfigSP()->queryProp("service/@visibility");
const char *certScope = strsame("cluster", visibility) ? "local" : "public";
Owned<IPropertyTree> info = getIssuerTlsServerConfig(certScope);
connectMethod = info ? SSLOnly : SSLNone;
connectMethod = hasIssuerTlsConfig(certScope) ? SSLOnly : SSLNone;
// NB: connectMethod will direct the CRemoteFileServer on accept to create a secure socket based on the same issuer certificates

dedicatedRowServicePort = 0; // row service always runs on same secure ssl port in containerized mode
Expand Down
30 changes: 15 additions & 15 deletions fs/dafsclient/rmtclient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,12 @@ static class _securitySettingsClient
}
}

const IPropertyTree * getSecureConfig()
{
//Later: return a synced tree...
return createSecureSocketConfig(queryCertificate(), queryPrivateKey(), queryPassPhrase());
}

protected:
DAFSConnectCfg connectMethod;
unsigned short daFileSrvPort;
Expand Down Expand Up @@ -156,10 +162,10 @@ static ISecureSocket *createSecureSocket(ISocket *sock, const char *issuer)
auto it = secureCtxClientIssuerMap.find(issuer);
if (it == secureCtxClientIssuerMap.end())
{
Owned<IPropertyTree> info = getIssuerTlsServerConfig(issuer);
if (!info)
Owned<const ISyncedPropertyTree> info = getIssuerTlsSyncedConfig(issuer);
if (!info || !info->isValid())
throw makeStringExceptionV(-1, "createSecureSocket() : missing MTLS configuration for issuer: %s", issuer);
secureContext.setown(createSecureSocketContextEx2(info, ClientSocket));
secureContext.setown(createSecureSocketContextSynced(info, ClientSocket));
secureCtxClientIssuerMap.emplace(issuer, secureContext.getLink());
}
else
Expand All @@ -168,7 +174,10 @@ static ISecureSocket *createSecureSocket(ISocket *sock, const char *issuer)
else
{
if (!secureContextClient)
secureContextClient.setown(createSecureSocketContextEx(securitySettings.queryCertificate(), securitySettings.queryPrivateKey(), securitySettings.queryPassPhrase(), ClientSocket));
{
Owned<const IPropertyTree> config = securitySettings.getSecureConfig();
secureContextClient.setown(createSecureSocketContextEx2(config, ClientSocket));
}
secureContext.set(secureContextClient);
}
}
Expand Down Expand Up @@ -722,17 +731,8 @@ void CRemoteBase::connectSocket(SocketEndpoint &ep, unsigned connectTimeoutMs, u
// The context would be 'owned' by the hook, and would expire when the mappings are removed (when removeMappedDafileSrvSecrets is called).
if (storageSecret)
{
Owned<IPropertyTree> secretPTree = getSecret("storage", storageSecret);
if (!secretPTree)
throw makeStringExceptionV(-1, "secret %s.%s not found", "storage", storageSecret.str());

StringBuffer certSecretBuf;
getSecretKeyValue(certSecretBuf, secretPTree, "tls.crt");

StringBuffer privKeySecretBuf;
getSecretKeyValue(privKeySecretBuf, secretPTree, "tls.key");

Owned<ISecureSocketContext> secureContext = createSecureSocketContextEx(certSecretBuf, privKeySecretBuf, nullptr, ClientSocket);
Owned<ISyncedPropertyTree> config = createStorageTlsConfig(storageSecret, false);
Owned<ISecureSocketContext> secureContext = createSecureSocketContextSynced(config, ClientSocket);
int loglevel = SSLogNormal;
#ifdef _DEBUG
loglevel = SSLogMax;
Expand Down
19 changes: 13 additions & 6 deletions fs/dafsserver/dafsserver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,13 @@ static class _securitySettingsServer
{
queryDafsSecSettings(&connectMethod, &daFileSrvPort, &daFileSrvSSLPort, &certificate, &privateKey, &passPhrase);
}

const IPropertyTree * getSecureConfig()
{
//Later: return a synced tree...
return createSecureSocketConfig(certificate, privateKey, passPhrase);
}

} securitySettings;
#endif

Expand All @@ -133,23 +140,23 @@ static ISecureSocket *createSecureSocket(ISocket *sock, bool disableClientCertVe
*/

const char *certScope = strsame("cluster", getComponentConfigSP()->queryProp("service/@visibility")) ? "local" : "public";
Owned<IPropertyTree> info = getIssuerTlsServerConfig(certScope);
if (!info)
Owned<const ISyncedPropertyTree> info = getIssuerTlsSyncedConfig(certScope);
if (!info || !info->isValid())
throw makeStringException(-1, "createSecureSocket() : missing MTLS configuration");
Owned<IPropertyTree> cloneInfo;
if (disableClientCertVerification)
{
// do not insist clients provide a cerificate for verification.
// do not insist clients provide a certificate for verification.
// This is used when the connection is TLS, but the authentication is done via other means
// e.g. in the case of the streaming service a opaque signed blob is transmitted and must
// be verified before proceeding.
cloneInfo.setown(createPTreeFromIPT(info));
cloneInfo->setPropBool("verify/@enable", false);
info = cloneInfo;
info.set(cloneInfo);
}
secureContextServer.setown(createSecureSocketContextEx2(info, ServerSocket));
secureContextServer.setown(createSecureSocketContextSynced(info, ServerSocket));
#else
secureContextServer.setown(createSecureSocketContextEx(securitySettings.certificate, securitySettings.privateKey, securitySettings.passPhrase, ServerSocket));
secureContextServer.setown(createSecureSocketContextEx2(securitySettings.getSecureConfig(), ServerSocket));
#endif
}
}
Expand Down
2 changes: 1 addition & 1 deletion roxie/ccd/ccd.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,7 @@ extern int backgroundCopyClass;
extern int backgroundCopyPrio;

extern unsigned roxiePort; // If listening on multiple, this is the first. Used for lock cascading
extern IPropertyTree *roxiePortTlsClientConfig;
extern ISyncedPropertyTree *roxiePortTlsClientConfig;


extern unsigned udpMulticastBufferSize;
Expand Down
4 changes: 2 additions & 2 deletions roxie/ccd/ccdlistener.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ class CascadeManager : public CInterface
CriticalSection revisionCrit;
int myEndpoint;
const IRoxieContextLogger &logctx;
IPropertyTree *tlsConfig = nullptr;
ISyncedPropertyTree *tlsConfig = nullptr;

void unlockChildren()
{
Expand Down Expand Up @@ -135,7 +135,7 @@ class CascadeManager : public CInterface
assertex(sock);
if (tlsConfig)
{
Owned<ISecureSocketContext> secureCtx = createSecureSocketContextEx2(tlsConfig, ClientSocket);
Owned<ISecureSocketContext> secureCtx = createSecureSocketContextSynced(tlsConfig, ClientSocket);
if (!secureCtx)
throw makeStringException(ROXIE_TLS_ERROR, "Roxie CascadeManager failed creating secure context for roxie control message");
Owned<ISecureSocket> ssock = secureCtx->createSecureSocket(sock.getClear());
Expand Down
6 changes: 3 additions & 3 deletions roxie/ccd/ccdmain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ unsigned leafCacheMB = 50;
unsigned blobCacheMB = 0;

unsigned roxiePort = 0;
IPropertyTree *roxiePortTlsClientConfig = nullptr;
ISyncedPropertyTree *roxiePortTlsClientConfig = nullptr;

#ifndef _CONTAINERIZED
Owned<IPerfMonHook> perfMonHook;
Expand Down Expand Up @@ -1501,7 +1501,7 @@ int CCD_API roxie_main(int argc, const char *argv[], const char * defaultYaml)
{
roxiePort = port;
if (roxieFarm.getPropBool("@tls"))
roxiePortTlsClientConfig = createIssuerTlsClientConfig(roxieFarm.queryProp("@issuer"), roxieFarm.getPropBool("@selfSigned"));
roxiePortTlsClientConfig = createIssuerTlsConfig(roxieFarm.queryProp("@issuer"), nullptr, true, roxieFarm.getPropBool("@selfSigned"), true);
debugEndpoint.set(roxiePort, ip);
}
bool suspended = roxieFarm.getPropBool("@suspended", false);
Expand All @@ -1513,7 +1513,7 @@ int CCD_API roxie_main(int argc, const char *argv[], const char * defaultYaml)
StringBuffer certFileName;
StringBuffer keyFileName;
StringBuffer passPhraseStr;
Owned<IPropertyTree> tlsConfig;
Owned<const IPropertyTree> tlsConfig;
if (serviceTLS)
{
protocol = "ssl";
Expand Down
11 changes: 7 additions & 4 deletions roxie/ccd/ccdprotocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -246,10 +246,13 @@ class ProtocolSocketListener : public ProtocolListener
#ifdef _USE_OPENSSL
if (isSSL)
{
if (_tlsConfig)
secureContext.setown(createSecureSocketContextEx2(_tlsConfig, ServerSocket));
else
secureContext.setown(createSecureSocketContextEx(certFile.get(), keyFile.get(), passPhrase.get(), ServerSocket));
Owned<IPropertyTree> config;
if (!_tlsConfig)
{
config.setown(createSecureSocketConfig(certFile.get(), keyFile.get(), passPhrase.get()));
_tlsConfig = config;
}
secureContext.setown(createSecureSocketContextEx2(_tlsConfig, ServerSocket));
}
#endif
}
Expand Down
Loading

0 comments on commit ee85cf9

Please sign in to comment.