Ver código fonte

HPCC-26763 Add support for ROXIE TLS to kubernetes HPCC

Signed-off-by: Anthony Fishbeck <anthony.fishbeck@lexisnexisrisk.com>
Anthony Fishbeck 3 anos atrás
pai
commit
a10ee35db1

+ 46 - 0
esp/clients/roxiecontrol.cpp

@@ -17,10 +17,12 @@
 
 #include "roxiecontrol.hpp"
 #include "jmisc.hpp"
+#include "securesocket.hpp"
 
 const unsigned roxieQueryRoxieTimeOut = 60000;
 
 #define EMPTY_RESULT_FAILURE 1200
+#define SECURE_CONNECTION_FAILURE 1201
 
 static void checkRoxieControlExceptions(IPropertyTree *response)
 {
@@ -117,3 +119,47 @@ IPropertyTree *sendRoxieControlAllNodes(const SocketEndpoint &ep, const char *ms
     Owned<ISocket> sock = ISocket::connect_timeout(ep, wait);
     return sendRoxieControlAllNodes(sock, msg, allOrNothing, wait);
 }
+
+static ISocket *createRoxieControlSocket(ISmartSocketFactory *conn, unsigned wait, unsigned connect_wait)
+{
+    const SocketEndpoint &ep = conn->nextEndpoint();
+    Owned<ISocket> sock = ISocket::connect_timeout(ep, connect_wait);
+    if (conn->isTlsService())
+    {
+#ifndef _USE_OPENSSL
+        throw makeStringException(SECURE_CONNECTION_FAILURE, "failed creating secure context for roxie control message OPENSSL not supported");
+#else
+        Owned<ISecureSocketContext> ownedSC = createSecureSocketContextSSF(conn);
+        if (!ownedSC)
+            throw makeStringException(SECURE_CONNECTION_FAILURE, "failed creating secure context for roxie control message");
+
+        Owned<ISecureSocket> ssock = ownedSC->createSecureSocket(sock.getClear());
+        if (!ssock)
+            throw makeStringException(SECURE_CONNECTION_FAILURE, "failed creating secure socket for roxie control message");
+
+        int status = ssock->secure_connect();
+        if (status < 0)
+        {
+            StringBuffer err;
+            err.append("Failure to establish secure connection to ");
+            ep.getUrlStr(err);
+            err.append(": returned ").append(status);
+            throw makeStringException(SECURE_CONNECTION_FAILURE, err.str());
+        }
+        return ssock.getClear();
+#endif
+    }
+    return sock.getClear();
+}
+
+IPropertyTree *sendRoxieControlQuery(ISmartSocketFactory *conn, const char *msg, unsigned wait, unsigned connect_wait)
+{
+    Owned<ISocket> sock = createRoxieControlSocket(conn, wait, connect_wait);
+    return sendRoxieControlQuery(sock, msg, wait);
+}
+
+IPropertyTree *sendRoxieControlAllNodes(ISmartSocketFactory *conn, const char *msg, bool allOrNothing, unsigned wait, unsigned connect_wait)
+{
+    Owned<ISocket> sock = createRoxieControlSocket(conn, wait, connect_wait);
+    return sendRoxieControlAllNodes(sock, msg, allOrNothing, wait);
+}

+ 10 - 1
esp/clients/roxiecontrol.hpp

@@ -21,12 +21,21 @@
 #include "jlib.hpp"
 #include "jsocket.hpp"
 #include "jptree.hpp"
-#include "roxiecontrol.hpp"
+#include "jsmartsock.hpp"
+
+const unsigned ROXIECONNECTIONTIMEOUT = 1000;   //1 second
+const unsigned ROXIECONTROLQUERYTIMEOUT = 3000; //3 second
+const unsigned ROXIECONTROLXREFTIMEOUT = 5000; //5 second
+const unsigned ROXIECONTROLQUERIESTIMEOUT = 30000; //30 second
+const unsigned ROXIELOCKCONNECTIONTIMEOUT = 60000; //60 second
 
 //bool sendRoxieControlLock(ISocket *sock, bool allOrNothing, unsigned wait)
 
+IPropertyTree *sendRoxieControlQuery(ISmartSocketFactory *conn, const char *msg, unsigned wait, unsigned connect_wait);
 IPropertyTree *sendRoxieControlQuery(ISocket *sock, const char *msg, unsigned wait);
 IPropertyTree *sendRoxieControlQuery(const SocketEndpoint &ep, const char *msg, unsigned wait);
+
+IPropertyTree *sendRoxieControlAllNodes(ISmartSocketFactory *conn, const char *msg, bool allOrNothing, unsigned wait, unsigned connect_wait);
 IPropertyTree *sendRoxieControlAllNodes(ISocket *sock, const char *msg, bool allOrNothing, unsigned wait);
 IPropertyTree *sendRoxieControlAllNodes(const SocketEndpoint &ep, const char *msg, bool allOrNothing, unsigned wait);
 

+ 2 - 2
esp/services/ws_dfu/ws_dfuXRefService.cpp

@@ -715,8 +715,8 @@ bool CWsDfuXRefEx::onDFUXRefUnusedFiles(IEspContext &context, IEspDFUXRefUnusedF
     if (!servers.length())
         throw MakeStringExceptionDirect(ECLWATCH_INVALID_CLUSTER_INFO, "process cluster, not found.");
 
-    Owned<ISocket> sock = ISocket::connect_timeout(servers.item(0), 5000);
-    Owned<IPropertyTree> controlXrefInfo = sendRoxieControlQuery(sock, "<control:getQueryXrefInfo/>", 5000);
+    Owned<ISocket> sock = ISocket::connect_timeout(servers.item(0), ROXIECONNECTIONTIMEOUT);
+    Owned<IPropertyTree> controlXrefInfo = sendRoxieControlQuery(sock, "<control:getQueryXrefInfo/>", ROXIECONTROLXREFTIMEOUT);
     if (!controlXrefInfo)
         throw MakeStringExceptionDirect(ECLWATCH_INTERNAL_ERROR, "roxie cluster, not responding.");
     MapStringTo<bool> usedFileMap;

+ 21 - 10
esp/services/ws_ecl/ws_ecl_service.cpp

@@ -196,22 +196,33 @@ static void appendServerAddress(StringBuffer &s, IPropertyTree &env, IPropertyTr
     s.append(netAddress).append(':').append(port ? port : "9876");
 }
 
+class WsEclSocketFactory : public CSmartSocketFactory
+{
+public:
+    bool includeTargetInURL;
+    StringAttr alias;
+
+    WsEclSocketFactory(IPropertyTree &service, bool _retry, bool includeTarget, const char *_alias, unsigned _dnsInterval) : CSmartSocketFactory(service, _retry, 60, _dnsInterval), includeTargetInURL(includeTarget), alias(_alias)
+    {
+    }
+
+    WsEclSocketFactory(const char *_socklist, bool _retry, bool includeTarget, const char *_alias, unsigned _dnsInterval) : CSmartSocketFactory(_socklist, _retry, 60, _dnsInterval), includeTargetInURL(includeTarget), alias(_alias)
+    {
+    }
+};
+
 void initContainerRoxieTargets(MapStringToMyClass<ISmartSocketFactory> &connMap)
 {
     Owned<IPropertyTreeIterator> services = getGlobalConfigSP()->getElements("services[@type='roxie']");
     ForEach(*services)
     {
         IPropertyTree &service = services->query();
-        const char *name = service.queryProp("@name");
-        const char *target = service.queryProp("@target");
-        const char *port = service.queryProp("@port");
 
-        if (isEmptyString(target) || isEmptyString(name)) //bad config?
+        const char *target = service.queryProp("@target");
+        if (isEmptyString(target) || isEmptyString(service.queryProp("@name"))) //bad config?
             continue;
 
-        StringBuffer s;
-        s.append(name).append(':').append(port ? port : "9876");
-        Owned<ISmartSocketFactory> sf = new RoxieSocketFactory(s.str(), false, true, nullptr, (unsigned) -1);
+        Owned<ISmartSocketFactory> sf = new WsEclSocketFactory(service, false, true, nullptr, (unsigned) -1);
         connMap.setValue(target, sf.get());
     }
 }
@@ -275,7 +286,7 @@ void initBareMetalRoxieTargets(MapStringToMyClass<ISmartSocketFactory> &connMap,
         if (list.length())
         {
             StringAttr alias(clusterInfo->getAlias());
-            Owned<ISmartSocketFactory> sf = new RoxieSocketFactory(list.str(), !loadBalanced, includeTargetInURL, loadBalanced ? alias.str() : NULL, dnsInterval);
+            Owned<ISmartSocketFactory> sf = new WsEclSocketFactory(list.str(), !loadBalanced, includeTargetInURL, loadBalanced ? alias.str() : NULL, dnsInterval);
             connMap.setValue(target.str(), sf.get());
             if (alias.length() && !connMap.getValue(alias.str())) //only need one vip per alias for routing purposes
                 connMap.setValue(alias.str(), sf.get());
@@ -2051,9 +2062,9 @@ void CWsEclBinding::sendRoxieRequest(const char *target, StringBuffer &req, Stri
         ep = conn->nextEndpoint();
 
         Owned<IHttpClientContext> httpctx = getHttpClientContext();
-        StringBuffer url("http://");
+        WsEclSocketFactory *roxieConn = static_cast<WsEclSocketFactory*>(conn);
+        StringBuffer url(roxieConn->isTlsService() ? "https://" : "http://");
         ep.getIpText(url).append(':').append(ep.port ? ep.port : 9876).append('/');
-        RoxieSocketFactory *roxieConn = static_cast<RoxieSocketFactory*>(conn);
         if (roxieConn->includeTargetInURL)
             url.append(roxieConn->alias.isEmpty() ? target : roxieConn->alias.str());
         if (!trim)

+ 0 - 11
esp/services/ws_ecl/ws_ecl_service.hpp

@@ -87,17 +87,6 @@ typedef enum wsEclTypes_
 
 } wsEclType;
 
-class RoxieSocketFactory : public CSmartSocketFactory
-{
-public:
-    bool includeTargetInURL;
-    StringAttr alias;
-
-    RoxieSocketFactory(const char *_socklist, bool _retry, bool includeTarget, const char *_alias, unsigned _dnsInterval) : CSmartSocketFactory(_socklist, _retry, 60, _dnsInterval), includeTargetInURL(includeTarget), alias(_alias)
-    {
-    }
-};
-
 class CWsEclService : public CInterface,
     implements IEspService
 {

+ 1 - 1
esp/services/ws_smc/ws_smcService.cpp

@@ -2305,7 +2305,7 @@ bool CWsSMCEx::onRoxieControlCmd(IEspContext &context, IEspRoxieControlCmdReques
     if (!isActiveK8sService(target))
         throw makeStringExceptionV(ECLWATCH_CANNOT_GET_ENV_INFO, "roxie target cluster has no active servers: %s", target);
 
-    Owned<IPropertyTree> controlResp = sendRoxieControlAllNodes(conn->nextEndpoint(), controlReq, true, req.getWait());
+    Owned<IPropertyTree> controlResp = sendRoxieControlAllNodes(conn, controlReq, true, req.getWait(), ROXIECONNECTIONTIMEOUT);
 #endif
     if (!controlResp)
         throw MakeStringException(ECLWATCH_INTERNAL_ERROR, "Failed to get control response from roxie.");

+ 15 - 17
esp/services/ws_workunits/ws_workunitsQuerySets.cpp

@@ -33,11 +33,6 @@
 
 #define DALI_FILE_LOOKUP_TIMEOUT (1000*15*1)  // 15 seconds
 
-const unsigned ROXIECONNECTIONTIMEOUT = 1000;   //1 second
-const unsigned ROXIECONTROLQUERYTIMEOUT = 3000; //3 second
-const unsigned ROXIECONTROLQUERIESTIMEOUT = 30000; //30 second
-const unsigned ROXIELOCKCONNECTIONTIMEOUT = 60000; //60 second
-
 //The CQuerySetQueryActionTypes[] has to match with the ESPenum QuerySetQueryActionTypes in the ecm file.
 static unsigned NumOfQuerySetQueryActionTypes = 7;
 static const char *QuerySetQueryActionTypes[] = { "Suspend", "Unsuspend", "ToggleSuspend", "Activate",
@@ -516,7 +511,7 @@ bool reloadCluster(MapStringToMyClass<ISmartSocketFactory> &roxieConnMap, const
 #ifndef _CONTAINERIZED
         Owned<IPropertyTree> result = sendRoxieControlAllNodes(addrs.item(0), "<control:reload/>", false, wait);
 #else
-        Owned<IPropertyTree> result = sendRoxieControlAllNodes(conn->nextEndpoint(), "<control:reload/>", false, wait);
+        Owned<IPropertyTree> result = sendRoxieControlAllNodes(conn, "<control:reload/>", false, wait, ROXIECONNECTIONTIMEOUT);
 #endif
         const char *status = result->queryProp("Endpoint[1]/Status");
         if (!status || !strieq(status, "ok"))
@@ -836,7 +831,7 @@ bool CWsWorkunitsEx::isQuerySuspended(const char* query, const char* target, uns
 #ifndef _CONTAINERIZED
         Owned<IPropertyTree> result = sendRoxieControlAllNodes(addrs.item(0), control.str(), false, wait);
 #else
-        Owned<IPropertyTree> result = sendRoxieControlAllNodes(conn->nextEndpoint(), control, false, wait);
+        Owned<IPropertyTree> result = sendRoxieControlAllNodes(conn, control, false, wait, ROXIECONNECTIONTIMEOUT);
 #endif
         if (!result)
             return false;
@@ -1282,13 +1277,16 @@ IPropertyTree *getQueriesOnCluster(const char *target, const char *queryset, Str
         }
 #ifndef _CONTAINERIZED
         Owned<ISocket> sock = ISocket::connect_timeout(eps.item(0), ROXIECONNECTIONTIMEOUT);
-#else
-        Owned<ISocket> sock = ISocket::connect_timeout(conn->nextEndpoint(), ROXIECONNECTIONTIMEOUT);
-#endif
         if (checkAllNodes)
             return sendRoxieControlAllNodes(sock, control, false, ROXIECONTROLQUERIESTIMEOUT);
         else
             return sendRoxieControlQuery(sock, control, ROXIECONTROLQUERIESTIMEOUT);
+#else
+        if (checkAllNodes)
+            return sendRoxieControlAllNodes(conn, control, false, ROXIECONTROLQUERIESTIMEOUT, ROXIECONNECTIONTIMEOUT);
+        else
+            return sendRoxieControlQuery(conn, control, ROXIECONTROLQUERIESTIMEOUT, ROXIECONNECTIONTIMEOUT);
+#endif
     }
     catch(IException* e)
     {
@@ -1462,10 +1460,10 @@ unsigned CWsWorkunitsEx::getGraphIdsByQueryId(const char *target, const char *qu
     VStringBuffer xpath("<control:querystats><Query id='%s'/></control:querystats>", queryId);
 #ifndef _CONTAINERIZED
     Owned<ISocket> sock = ISocket::connect_timeout(eps.item(0), ROXIECONNECTIONTIMEOUT);
+    Owned<IPropertyTree> querystats = sendRoxieControlQuery(sock, xpath.str(), ROXIECONTROLQUERYTIMEOUT);
 #else
-    Owned<ISocket> sock = ISocket::connect_timeout(conn->nextEndpoint(), ROXIECONNECTIONTIMEOUT);
+    Owned<IPropertyTree> querystats = sendRoxieControlQuery(conn, xpath.str(), ROXIECONTROLQUERYTIMEOUT, ROXIECONNECTIONTIMEOUT);
 #endif
-    Owned<IPropertyTree> querystats = sendRoxieControlQuery(sock, xpath.str(), ROXIECONTROLQUERYTIMEOUT);
     if (!querystats)
         return 0;
 
@@ -3422,7 +3420,7 @@ void CWsWorkunitsEx::getGraphsByQueryId(const char *target, const char *queryId,
 
     PROGLOG("getGraphsByQueryId: target %s, query %s", target, queryId);
     VStringBuffer control("<control:querystats><Query id='%s'/></control:querystats>", queryId);
-    Owned<IPropertyTree> querystats = sendRoxieControlAllNodes(conn->nextEndpoint(), control.str(), false, ROXIELOCKCONNECTIONTIMEOUT);
+    Owned<IPropertyTree> querystats = sendRoxieControlAllNodes(conn, control.str(), false, ROXIELOCKCONNECTIONTIMEOUT, ROXIECONNECTIONTIMEOUT);
 #endif
     if (!querystats)
         return;
@@ -3543,14 +3541,14 @@ IPropertyTree* CWsWorkunitsEx::sendControlQuery(IEspContext& context, const char
         throw MakeStringException(ECLWATCH_INVALID_CLUSTER_NAME, "CWsWorkunitsEx::sendControlQuery: Server not found for %s", target);
 
     Owned<ISocket> sock = ISocket::connect_timeout(eps.item(0), timeout);
+    return sendRoxieControlQuery(sock, query, timeout);
 #else
     ISmartSocketFactory *conn = roxieConnMap.getValue(target);
     if (!conn)
         throw makeStringExceptionV(ECLWATCH_CANNOT_GET_ENV_INFO, "roxie target cluster not mapped: %s", target);
 
-    Owned<ISocket> sock = ISocket::connect_timeout(conn->nextEndpoint(), timeout);
+    return sendRoxieControlQuery(conn, query, timeout, ROXIECONNECTIONTIMEOUT);
 #endif
-    return sendRoxieControlQuery(sock, query, timeout);
 }
 
 bool CWsWorkunitsEx::onWUUpdateQueryEntry(IEspContext& context, IEspWUUpdateQueryEntryRequest& req, IEspWUUpdateQueryEntryResponse& resp)
@@ -3618,7 +3616,7 @@ bool CWsWorkunitsEx::onWUGetNumFileToCopy(IEspContext& context, IEspWUGetNumFile
                 PROGLOG("WUGetNumFileToCopy: Process Server not found for %s", clusterName.get());
                 return nullptr;
             }
-            Owned<IPropertyTree> result = sendRoxieControlAllNodes(conn->nextEndpoint(), "<control:numfilestoprocess/>", false, ROXIELOCKCONNECTIONTIMEOUT);
+            Owned<IPropertyTree> result = sendRoxieControlAllNodes(conn, "<control:numfilestoprocess/>", false, ROXIELOCKCONNECTIONTIMEOUT, ROXIECONNECTIONTIMEOUT);
 #endif
             if (!result)
             {
@@ -3763,7 +3761,7 @@ bool CWsWorkunitsEx::onWUQueryGetSummaryStats(IEspContext& context, IEspWUQueryG
 #ifndef _CONTAINERIZED
         Owned<IPropertyTree> queryAggregates = sendRoxieControlAllNodes(eps.item(0), control.str(), false, ROXIELOCKCONNECTIONTIMEOUT);
 #else
-        Owned<IPropertyTree> queryAggregates = sendRoxieControlAllNodes(conn->nextEndpoint(), control, false, ROXIELOCKCONNECTIONTIMEOUT);
+        Owned<IPropertyTree> queryAggregates = sendRoxieControlAllNodes(conn, control, false, ROXIELOCKCONNECTIONTIMEOUT, ROXIECONNECTIONTIMEOUT);
 #endif
         if (!queryAggregates)
         {

+ 4 - 6
esp/smc/SMCLib/TpContainer.cpp

@@ -29,6 +29,7 @@
 #include "dautils.hpp"
 #include "dameta.hpp"
 #include "hpccconfig.hpp"
+#include "securesocket.hpp"
 
 static CConfigUpdateHook configUpdateHook;
 
@@ -689,16 +690,13 @@ extern TPWRAPPER_API void initContainerRoxieTargets(MapStringToMyClass<ISmartSoc
     ForEach(*services)
     {
         IPropertyTree& service = services->query();
-        const char* name = service.queryProp("@name");
         const char* target = service.queryProp("@target");
-        const char* port = service.queryProp("@port");
 
-        if (isEmptyString(target) || isEmptyString(name)) //bad config?
+        if (isEmptyString(target) || isEmptyString(service.queryProp("@name"))) //bad config?
             continue;
 
-        StringBuffer s;
-        s.append(name).append(':').append(port ? port : "9876");
-        Owned<ISmartSocketFactory> sf = new CSmartSocketFactory(s.str(), false, 60, (unsigned) -1);
+        bool tls = service.getPropBool("@tls", false);
+        Owned<ISmartSocketFactory> sf = tls ? createSecureSmartSocketFactory(service) : createSmartSocketFactory(service);
         connMap.setValue(target, sf.get());
     }
 }

+ 4 - 6
esp/smc/SMCLib/TpWrapper.cpp

@@ -22,6 +22,7 @@
 
 #include "TpWrapper.hpp"
 #include <stdio.h>
+#include "securesocket.hpp"
 #include "workunit.hpp"
 #include "exception_util.hpp"
 #include "portlist.h"
@@ -2069,16 +2070,13 @@ extern TPWRAPPER_API void initContainerRoxieTargets(MapStringToMyClass<ISmartSoc
     ForEach(*services)
     {
         IPropertyTree& service = services->query();
-        const char* name = service.queryProp("@name");
         const char* target = service.queryProp("@target");
-        const char* port = service.queryProp("@port");
 
-        if (isEmptyString(target) || isEmptyString(name)) //bad config?
+        if (isEmptyString(target) || isEmptyString(service.queryProp("@name"))) //bad config?
             continue;
 
-        StringBuffer s;
-        s.append(name).append(':').append(port ? port : "9876");
-        Owned<ISmartSocketFactory> sf = new CSmartSocketFactory(s.str(), false, 60, (unsigned) -1);
+        bool tls = service.getPropBool("@tls", false);
+        Owned<ISmartSocketFactory> sf = tls ? createSecureSmartSocketFactory(service) : createSmartSocketFactory(service);
         connMap.setValue(target, sf.get());
     }
 }

+ 38 - 9
helm/hpcc/templates/_helpers.tpl

@@ -712,6 +712,40 @@ Generate instance queue names
 {{- end -}}
 
 {{/*
+Generate service entries for TLS
+*/}}
+{{- define "hpcc.addTLSServiceEntries" -}}
+  {{- $externalService := (ne ( include "hpcc.isVisibilityPublic" (dict "root" .root "visibility" .visibility)) "") }}
+  public: {{ $externalService | ternary "true" "false" }}
+  {{- if (hasKey .service "tls") }}
+  tls: {{ .service.tls }}
+  {{- else -}}
+    {{- if and ($externalService) (hasKey .component "certificate") }}
+  tls: true
+    {{- else }}
+      {{- $issuerName := ternary "public" "local" $externalService }}
+      {{- $certificates := (.root.Values.certificates | default dict) -}}
+      {{- if not $certificates.enabled }}
+  tls: false
+      {{- else -}}
+        {{- $issuers := ($certificates.issuers | default dict) -}}
+        {{- $issuer := get $issuers $issuerName -}}
+        {{- if not $issuer }}
+  tls: false
+        {{- else -}}
+          {{- $issuerSpec := ($issuer.spec | default dict) }}
+  tls: {{ (hasKey $issuer "enabled" | ternary $issuer.enabled true) }}
+  issuer: {{ $issuerName }}
+  selfSigned: {{ (hasKey $issuerSpec "selfSigned") }}
+  caCert: {{ (or (hasKey $issuerSpec "ca") (hasKey $issuerSpec "vault")) }}
+        {{- end -}}
+      {{- end -}}
+    {{- end }}
+  {{- end }}
+{{- end }}
+
+
+{{/*
 Generate list of available services
 */}}
 {{- define "hpcc.generateConfigMapServices" -}}
@@ -724,22 +758,17 @@ Generate list of available services
   type: roxie
   port: {{ $service.servicePort }}
   target: {{ $roxie.name }}
-  public: {{ (ne ( include "hpcc.isVisibilityPublic" (dict "root" $ "visibility" $service.visibility)) "") | ternary "true" "false" }}
-   {{- end -}}
-  {{- end }}
+  {{- include "hpcc.addTLSServiceEntries" (dict "root" $ "service" $service "component" $roxie "visibility" $service.visibility) }}
 {{ end -}}
+  {{- end }}
+ {{- end -}}
 {{- end -}}
 {{- range $esp := $.Values.esp -}}
 - name: {{ $esp.name }}
   class: esp
   type: {{ $esp.application }}
   port: {{ $esp.service.servicePort }}
-  {{- if hasKey $esp "tls" }}
-  tls: {{ $esp.tls }}
-  {{- else }}
-  tls: {{ ($.Values.certificates | default dict).enabled }}
-  {{- end }}
-  public: {{ (ne ( include "hpcc.isVisibilityPublic" (dict "root" $ "visibility" $esp.service.visibility))  "") | ternary "true" "false" }}
+  {{- include "hpcc.addTLSServiceEntries" (dict "root" $ "service" $esp "component" $esp "visibility" $esp.service.visibility) }}
 {{ end -}}
 {{- range $dali := $.Values.dali -}}
 {{- $sashaServices := $dali.services | default dict -}}

+ 3 - 2
helm/hpcc/templates/esp.yaml

@@ -83,8 +83,9 @@ data:
 {{- else -}}
  {{- $_ := set $commonCtx "externalCert" false -}}
 {{- end -}}
-{{- $generateSigningCert := and (eq $application "eclwatch") (hasKey $.Values.certificates.issuers "signing") -}}
-{{- $signingCertGenerator := or ($generateSigningCert) (has $application (list "eclservices")) | ternary "eclwatch" "" -}}
+{{- $signingEnabled := eq (include "hpcc.isIssuerEnabled" (dict "root" $ "issuer" "signing")) "true" -}}
+{{- $generateSigningCert := and ($signingEnabled) (eq $application "eclwatch") -}}
+{{- $signingCertGenerator := and ($signingEnabled) (has $application (list "eclwatch" "eclservices")) | ternary "eclwatch" "" -}}
 apiVersion: apps/v1
 kind: Deployment
 metadata:

+ 3 - 0
helm/hpcc/templates/issuers.yaml

@@ -22,6 +22,8 @@
 
 */}}
 {{- define "hpcc.issuers" -}}
+{{- $issuerEnabled := (hasKey .me "enabled" | ternary .me.enabled true) -}}
+{{- if $issuerEnabled }}
 apiVersion: cert-manager.io/v1
 kind: {{ .me.kind | default "Issuer" }}
 metadata:
@@ -42,6 +44,7 @@ spec:
 
  {{- end }}
 {{- end }}
+{{- end }}
 
 {{- template "hpcc.ensureNoResourceValidationFlag" ( dict "root" $ ) }}
 

+ 16 - 5
helm/hpcc/templates/roxie.yaml

@@ -21,6 +21,7 @@
 ##############################################################################
 
 */}}
+
 {{/*
 roxie configmap
 Pass in dict with root and me
@@ -33,7 +34,17 @@ data:
   {{ .me.name }}.yaml:
     version: 1.0
     roxie:
-{{ toYaml ( omit .me "logging" "topoServer" "encryptInTransit" "env") | indent 6 }}
+{{- $root := .root -}}
+{{- $component := .me }}
+      services:
+  {{- range $service := .me.services }}
+      - name: {{ $service.name }}
+{{ toYaml (omit $service "tls" "name") | indent 8 }}
+   {{- if ne (int $service.servicePort) 0 }}
+     {{- include "hpcc.addTLSServiceEntries" (dict "root" $root "service" $service "component" $component "visibility" $service.visibility) | indent 6 }}
+   {{- end }}
+  {{- end }}
+{{ toYaml ( omit .me "logging" "topoServer" "encryptInTransit" "env" "services") | indent 6 }}
       numChannels: {{ .numChannels }}
       topologyServers: "{{ .toponame }}:{{ .topoport }}"
       heartbeatInterval: {{ .heartbeatInterval }}
@@ -286,14 +297,14 @@ spec:
 {{ include "hpcc.addVolumeMounts" $commonCtx | indent 8 }}
 {{ include "hpcc.addSecretVolumeMounts" $commonCtx | indent 8 }}
 {{ include "hpcc.addCertificateVolumeMount" (dict "root" $ "component" "roxie-server" "name" $servername "external" false) | indent 8 }}
-{{ include "hpcc.addCertificateVolumeMount" (dict "root" $ "component" "roxie-server" "name" $servername "external" true) | indent 8 }}
+{{ include "hpcc.addCertificateVolumeMount" (dict "root" $ "component" "roxie-server" "name" $servername "certificate" $roxie.certificate "external" true) | indent 8 }}
 {{ include "hpcc.addUDPCertificateVolumeMount" (dict "root" $ "component" "udpkey" "name" $udpkeyname ) | indent 8 }}
       volumes:
 {{ include "hpcc.addConfigMapVolume" $roxie | indent 6 }}
 {{ include "hpcc.addVolumes" $commonCtx | indent 6 }}
 {{ include "hpcc.addSecretVolumes" $commonCtx | indent 6 }}
 {{ include "hpcc.addCertificateVolume" (dict "root" $ "component" "roxie-server" "name" $servername "external" false) | indent 6 }}
-{{ include "hpcc.addCertificateVolume" (dict "root" $ "component" "roxie-server" "name" $servername "external" true) | indent 6 }}
+{{ include "hpcc.addCertificateVolume" (dict "root" $ "component" "roxie-server" "name" $servername "certificate" $roxie.certificate "external" true) | indent 6 }}
 {{ include "hpcc.addUDPCertificateVolume" (dict "root" $ "component" "udpkey" "name" $udpkeyname) | indent 6 }}
 
 ---
@@ -396,7 +407,7 @@ spec:
 {{ include "hpcc.addSecretVolumeMounts" $commonCtx | indent 8 }}
 {{- if not $roxie.serverReplicas }}
 {{ include "hpcc.addCertificateVolumeMount" (dict "root" $ "component" "roxie-agent" "name" $name "external" false) | indent 8 }}
-{{ include "hpcc.addCertificateVolumeMount" (dict "root" $ "component" "roxie-agent" "name" $name "external" true) | indent 8 }}
+{{ include "hpcc.addCertificateVolumeMount" (dict "root" $ "component" "roxie-agent" "name" $name "certificate" $roxie.certificate "external" true) | indent 8 }}
 {{ include "hpcc.addUDPCertificateVolumeMount" (dict "root" $ "component" "udpkey" "name" $udpkeyname ) | indent 8 }}
 {{- end }}
 
@@ -406,7 +417,7 @@ spec:
 {{ include "hpcc.addSecretVolumes" $commonCtx | indent 6 }}
 {{- if not $roxie.serverReplicas }}
 {{ include "hpcc.addCertificateVolume" (dict "root" $ "component" "roxie-agent" "name" $name "external" false) | indent 6 }}
-{{ include "hpcc.addCertificateVolume" (dict "root" $ "component" "roxie-agent" "name" $name "external" true) | indent 6 }}
+{{ include "hpcc.addCertificateVolume" (dict "root" $ "component" "roxie-agent" "name" $name "certificate" $roxie.certificate "external" true) | indent 6 }}
 {{ include "hpcc.addUDPCertificateVolume" (dict "root" $ "component" "udpkey" "name" $udpkeyname) | indent 6 }}
 {{- end }}
 

+ 12 - 0
helm/hpcc/values.schema.json

@@ -1003,6 +1003,10 @@
         },
         "service": {
           "$ref": "#/definitions/service"
+        },
+        "certificate": {
+          "type": "string",
+          "description": "Name of the secret which contains the TLS certificate.  Custom configuration instead of using, or overriding cert-manager certificate."
         }
       }
     },
@@ -1125,6 +1129,10 @@
         "labels": {
           "type": "object",
           "additionalProperties": { "type": "string" }
+        },
+        "certificate": {
+          "type": "string",
+          "description": "Name of the secret which contains the TLS certificate.  Custom configuration instead of using, or overriding cert-manager certificate."
         }
       }
     },
@@ -1191,6 +1199,10 @@
         "visibility": {
           "type": "string",
           "description": "Should this service be exposed outside the cluster, locally or to the internet"
+        },
+        "tls": {
+          "type": "boolean",
+          "description": "Whether the roxie service uses tls.  Requires cert-manager or custom certificate."
         }
       },
       "required": [ "name", "servicePort" ],

+ 2 - 0
roxie/ccd/ccd.hpp

@@ -393,6 +393,8 @@ 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 unsigned udpMulticastBufferSize;
 extern size32_t diskReadBufferSize;

+ 25 - 3
roxie/ccd/ccdlistener.cpp

@@ -19,6 +19,7 @@
 #include "jlib.hpp"
 #include "jthread.hpp"
 #include "jregexp.hpp"
+#include "securesocket.hpp"
 
 #include "wujobq.hpp"
 #include "thorplugin.hpp"
@@ -77,6 +78,7 @@ class CascadeManager : public CInterface
     CriticalSection revisionCrit;
     int myEndpoint;
     const IRoxieContextLogger &logctx;
+    IPropertyTree *tlsConfig = nullptr;
 
     void unlockChildren()
     {
@@ -129,9 +131,29 @@ class CascadeManager : public CInterface
                     ep.getUrlStr(epStr);
                     DBGLOG("connectChild connecting to %s", epStr.str());
                 }
-                ISocket *sock = ISocket::connect_timeout(ep, 2000);
+                Owned<ISocket> sock = ISocket::connect_timeout(ep, 2000);
                 assertex(sock);
-                activeChildren.append(*sock);
+                if (tlsConfig)
+                {
+                    Owned<ISecureSocketContext> secureCtx = createSecureSocketContextEx2(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());
+                    if (!ssock)
+                        throw makeStringException(ROXIE_TLS_ERROR, "Roxie CascadeManager failed creating secure socket for roxie control message");
+
+                    int status = ssock->secure_connect();
+                    if (status < 0)
+                    {
+                        StringBuffer err;
+                        err.append("Roxie CascadeManager failed to establish secure connection to ");
+                        ep.getUrlStr(err);
+                        err.append(": returned ").append(status);
+                        throw makeStringException(ROXIE_TLS_ERROR, err.str());
+                    }
+                    sock.setown(ssock.getClear());
+                }
+                activeChildren.append(*sock.getClear());
                 activeIdxes.append(idx);
                 if (traceLevel)
                 {
@@ -289,7 +311,7 @@ private:
 
 
 public:
-    CascadeManager(const IRoxieContextLogger &_logctx, const ITopologyServer *_topology) : topology(_topology), servers(_topology->queryServers(roxiePort)), logctx(_logctx)
+    CascadeManager(const IRoxieContextLogger &_logctx, const ITopologyServer *_topology) : topology(_topology), servers(_topology->queryServers(roxiePort)), logctx(_logctx), tlsConfig(roxiePortTlsClientConfig)
     {
         entered = false;
         connected = false;

+ 25 - 9
roxie/ccd/ccdmain.cpp

@@ -214,6 +214,8 @@ unsigned leafCacheMB = 50;
 unsigned blobCacheMB = 0;
 
 unsigned roxiePort = 0;
+IPropertyTree *roxiePortTlsClientConfig = nullptr;
+
 #ifndef _CONTAINERIZED
 Owned<IPerfMonHook> perfMonHook;
 #endif
@@ -221,12 +223,14 @@ Owned<IPerfMonHook> perfMonHook;
 MODULE_INIT(INIT_PRIORITY_STANDARD)
 {
     topology = NULL;
+    roxiePortTlsClientConfig = nullptr;
 
     return true;
 }
 
 MODULE_EXIT()
 {
+    ::Release(roxiePortTlsClientConfig);
     ::Release(topology);
 }
 
@@ -1348,6 +1352,8 @@ int CCD_API roxie_main(int argc, const char *argv[], const char * defaultYaml)
                     if (!roxiePort)
                     {
                         roxiePort = port;
+                        if (roxieFarm.getPropBool("@tls"))
+                            roxiePortTlsClientConfig = createTlsClientSecretInfo(roxieFarm.queryProp("@issuer"), !roxieFarm.getPropBool("@public"), roxieFarm.getPropBool("@selfSigned"));
                         debugEndpoint.set(roxiePort, ip);
                     }
                     bool suspended = roxieFarm.getPropBool("@suspended", false);
@@ -1355,12 +1361,23 @@ int CCD_API roxie_main(int argc, const char *argv[], const char * defaultYaml)
                     if (port)
                     {
                         const char *protocol = roxieFarm.queryProp("@protocol");
+                        bool serviceTLS  = roxieFarm.getPropBool("@tls") || (protocol && streq(protocol, "ssl"));
                         StringBuffer certFileName;
                         StringBuffer keyFileName;
                         StringBuffer passPhraseStr;
-                        if (protocol && streq(protocol, "ssl"))
+                        if (serviceTLS)
                         {
-    #ifdef _USE_OPENSSL
+                            protocol = "ssl";
+#ifdef _USE_OPENSSL
+    #ifdef _CONTAINERIZED
+                            const char *certIssuer = roxieFarm.getPropBool("@public", true) ? "public" : "local";
+                            certFileName.setf("/opt/HPCCSystems/secrets/certificates/%s/tls.crt", certIssuer);
+                            keyFileName.setf("/opt/HPCCSystems/secrets/certificates/%s/tls.key", certIssuer);
+    #else
+                            const char *passPhrase = roxieFarm.queryProp("@passphrase");
+                            if (!isEmptyString(passPhrase))
+                                decrypt(passPhraseStr, passPhrase);
+
                             const char *certFile = roxieFarm.queryProp("@certificateFileName");
                             if (!certFile)
                                 throw MakeStringException(ROXIE_FILE_ERROR, "Roxie SSL Farm Listener on port %d missing certificateFileName tag", port);
@@ -1368,8 +1385,6 @@ int CCD_API roxie_main(int argc, const char *argv[], const char * defaultYaml)
                                 certFileName.append(certFile);
                             else
                                 certFileName.append(codeDirectory.str()).append(certFile);
-                            if (!checkFileExists(certFileName.str()))
-                                throw MakeStringException(ROXIE_FILE_ERROR, "Roxie SSL Farm Listener on port %d missing certificateFile (%s)", port, certFileName.str());
 
                             const char *keyFile = roxieFarm.queryProp("@privateKeyFileName");
                             if (!keyFile)
@@ -1378,16 +1393,17 @@ int CCD_API roxie_main(int argc, const char *argv[], const char * defaultYaml)
                                 keyFileName.append(keyFile);
                             else
                                 keyFileName.append(codeDirectory.str()).append(keyFile);
+    #endif
+                            if (!checkFileExists(certFileName.str()))
+                                throw MakeStringException(ROXIE_FILE_ERROR, "Roxie SSL Farm Listener on port %d missing certificateFile (%s)", port, certFileName.str());
+
                             if (!checkFileExists(keyFileName.str()))
                                 throw MakeStringException(ROXIE_FILE_ERROR, "Roxie SSL Farm Listener on port %d missing privateKeyFile (%s)", port, keyFileName.str());
 
-                            const char *passPhrase = roxieFarm.queryProp("@passphrase");
-                            if (!isEmptyString(passPhrase))
-                                decrypt(passPhraseStr, passPhrase);
-    #else
+#else
                             OWARNLOG("Skipping Roxie SSL Farm Listener on port %d : OpenSSL disabled in build", port);
                             continue;
-    #endif
+#endif
                         }
                         const char *soname =  roxieFarm.queryProp("@so");
                         const char *config  = roxieFarm.queryProp("@config");

+ 1 - 0
roxie/roxie/roxie.hpp

@@ -86,6 +86,7 @@
 #define ROXIE_MISSING_DLL           ROXIE_ERROR_START+64
 #define ROXIE_INVALID_TARGET        ROXIE_ERROR_START+65
 #define ROXIE_LAYOUT_MISMATCH       ROXIE_ERROR_START+66
+#define ROXIE_TLS_ERROR             ROXIE_ERROR_START+67
 
 // Aeron layer errors
 

+ 41 - 0
system/jlib/jsecrets.cpp

@@ -764,6 +764,47 @@ const MemoryAttr &getSecretUdpKey(bool required)
     return udpKey;
 }
 
+IPropertyTree *createTlsClientSecretInfo(const char *issuer, bool mutual, bool acceptSelfSigned, bool addCACert)
+{
+    if (isEmptyString(issuer))
+        return nullptr;
+
+    StringBuffer filepath;
+    StringBuffer secretpath;
+    buildSecretPath(secretpath, "certificates", issuer);
+
+    Owned<IPropertyTree> info = createPTree();
+
+    if (mutual)
+    {
+        filepath.set(secretpath).append("tls.crt");
+        if (!checkFileExists(filepath))
+            return nullptr;
+
+        info->setProp("certificate", filepath.str());
+        filepath.set(secretpath).append("tls.key");
+        if (checkFileExists(filepath))
+            info->setProp("privatekey", filepath.str());
+    }
+
+    IPropertyTree *verify = ensurePTree(info, "verify");
+    if (addCACert)
+    {
+        filepath.set(secretpath).append("ca.crt");
+        if (checkFileExists(filepath))
+        {
+            IPropertyTree *ca = ensurePTree(verify, "ca_certificates");
+            ca->setProp("@path", filepath.str());
+        }
+    }
+    verify->setPropBool("@enable", true);
+    verify->setPropBool("@address_match", false);
+    verify->setPropBool("@accept_selfsigned", acceptSelfSigned);
+    verify->setProp("trusted_peers", "anyone");
+
+    return info.getClear();
+}
+
 IPropertyTree *queryTlsSecretInfo(const char *name)
 {
     if (isEmptyString(name))

+ 2 - 1
system/jlib/jsecrets.hpp

@@ -36,7 +36,8 @@ extern jlib_decl bool getSecretValue(StringBuffer & result, const char *category
 extern jlib_decl void initSecretUdpKey();
 extern jlib_decl const MemoryAttr &getSecretUdpKey(bool required);
 
-extern jlib_decl IPropertyTree *queryTlsSecretInfo(const char *name);
+extern jlib_decl IPropertyTree *queryTlsSecretInfo(const char *issuer);
+extern jlib_decl IPropertyTree *createTlsClientSecretInfo(const char *issuer, bool mutual, bool acceptSelfSigned, bool addCACert=true);
 
 extern jlib_decl  void splitFullUrl(const char *url, bool &https, StringBuffer &user, StringBuffer &password, StringBuffer &host, StringBuffer &port, StringBuffer &fullpath);
 extern jlib_decl void splitUrlSchemeHostPort(const char *url, StringBuffer &user, StringBuffer &password, StringBuffer &schemeHostPort, StringBuffer &path);

+ 40 - 2
system/jlib/jsmartsock.cpp

@@ -17,6 +17,7 @@
 
 
 #include "jsmartsock.ipp"
+#include "jsecrets.hpp"
 #include "jdebug.hpp"
 
 ISmartSocketException *createSmartSocketException(int errorCode, const char *msg)
@@ -207,6 +208,38 @@ void CSmartSocket::close()
     }
 }
 
+CSmartSocketFactory::CSmartSocketFactory(IPropertyTree &service, bool _retry, unsigned _retryInterval, unsigned _dnsInterval)
+{
+    const char *name = service.queryProp("@name");
+    const char *port = service.queryProp("@port");
+    if (isEmptyString(name) || isEmptyString(port))
+        throw createSmartSocketException(0, "CSmartSocket factory both name and port required for service configuration");
+
+    tlsService  = service.getPropBool("@tls");
+    if (tlsService)
+        tlsConfig.setown(createTlsClientSecretInfo(service.queryProp("@issuer"), !service.getPropBool("@pulic"), service.getPropBool("@selfSigned"), service.getPropBool("@caCert")));
+
+    StringBuffer s;
+    s.append(name).append(':').append(port);
+
+    PROGLOG("CSmartSocketFactory::CSmartSocketFactory(service(%s), %s)", name, s.str());
+
+    SmartSocketListParser slp(s);
+    if (slp.getSockets(sockArray) == 0)
+        throw createSmartSocketException(0, "no endpoints defined");
+
+    shuffleEndpoints();
+
+    nextEndpointIndex = 0;
+    dnsInterval=_dnsInterval;
+
+    retry = _retry;
+    if (retry)
+    {
+        retryInterval = _retryInterval;
+        this->start();
+    }
+}
 
 CSmartSocketFactory::CSmartSocketFactory(const char *_socklist, bool _retry, unsigned _retryInterval, unsigned _dnsInterval)
 {
@@ -228,7 +261,6 @@ CSmartSocketFactory::CSmartSocketFactory(const char *_socklist, bool _retry, uns
     }
 }
 
-
 CSmartSocketFactory::~CSmartSocketFactory()
 {
     stop();
@@ -449,6 +481,12 @@ StringBuffer & CSmartSocketFactory::getUrlStr(StringBuffer &url, bool useHostNam
     return url;
 }
 
-ISmartSocketFactory *createSmartSocketFactory(const char *_socklist, bool _retry, unsigned _retryInterval, unsigned _dnsInterval) {
+ISmartSocketFactory *createSmartSocketFactory(IPropertyTree &service, bool _retry, unsigned _retryInterval, unsigned _dnsInterval)
+{
+    return new CSmartSocketFactory(service, _retry, _retryInterval, _dnsInterval);
+}
+
+ISmartSocketFactory *createSmartSocketFactory(const char *_socklist, bool _retry, unsigned _retryInterval, unsigned _dnsInterval)
+{
     return new CSmartSocketFactory(_socklist, _retry, _retryInterval, _dnsInterval);
 }

+ 3 - 1
system/jlib/jsmartsock.hpp

@@ -58,6 +58,8 @@ interface jlib_decl ISmartSocketFactory : extends IInterface
     virtual void resolveHostnames() = 0;
 
     virtual StringBuffer & getUrlStr(StringBuffer &str, bool useHostName) = 0;
+    virtual bool isTlsService() const = 0;
+    virtual const IPropertyTree *queryTlsConfig() const = 0;
 };
 
 
@@ -65,7 +67,7 @@ interface jlib_thrown_decl ISmartSocketException : extends IException
 {
 };
 
-
+jlib_decl ISmartSocketFactory *createSmartSocketFactory(IPropertyTree &service, bool _retry = false, unsigned _retryInterval = 60, unsigned _dnsInterval = (unsigned) -1);
 jlib_decl ISmartSocketFactory *createSmartSocketFactory(const char *_socklist, bool _retry = false, unsigned _retryInterval = 60, unsigned _dnsInterval = (unsigned) -1);
 
 jlib_decl ISmartSocketException *createSmartSocketException(int errorCode, const char *msg);

+ 6 - 0
system/jlib/jsmartsock.ipp

@@ -66,6 +66,9 @@ class jlib_decl CSmartSocketFactory: public Thread,
 
     unsigned nextEndpointIndex;
     bool retry;
+    bool tlsService = false;
+    Owned<IPropertyTree> tlsConfig;
+
     unsigned retryInterval;
     unsigned dnsInterval;
 
@@ -76,6 +79,7 @@ public:
     IMPLEMENT_IINTERFACE;
 
     CSmartSocketFactory(const char *_socklist, bool _retry = false, unsigned _retryInterval = 60, unsigned _dnsInterval = (unsigned)-1);
+    CSmartSocketFactory(IPropertyTree &service, bool _retry = false, unsigned _retryInterval = 60, unsigned _dnsInterval = (unsigned)-1);
     ~CSmartSocketFactory();
     int run();
 
@@ -97,6 +101,8 @@ public:
     virtual void resolveHostnames();
 
     virtual StringBuffer & getUrlStr(StringBuffer &str, bool useHostName);
+    virtual bool isTlsService() const override { return tlsService; }
+    virtual const IPropertyTree *queryTlsConfig() const { return tlsConfig; };
 };
 
 

+ 37 - 11
system/security/securesocket/securesocket.cpp

@@ -63,8 +63,6 @@
 
 static JSocketStatistics *SSTATS;
 
-bool accept_selfsigned = false;
-
 #define CHK_NULL(x) if((x)==NULL) exit(1)
 #define CHK_ERR(err, s) if((err)==-1){perror(s);exit(1);}
 #define CHK_SSL(err) if((err) ==-1){ERR_print_errors_fp(stderr); exit(2);}
@@ -1085,7 +1083,7 @@ bool CSecureSocket::send_block(const void *blk, size32_t sz)
 
 // ----------------------------
 
-int verify_callback(int ok, X509_STORE_CTX *store)
+int verify_callback(int ok, X509_STORE_CTX *store, bool accept_selfsigned)
 {
     if(!ok)
     {
@@ -1107,6 +1105,16 @@ int verify_callback(int ok, X509_STORE_CTX *store)
     return ok;
 }
 
+int verify_callback_allow_selfSigned(int ok, X509_STORE_CTX *store)
+{
+    return verify_callback(ok, store, true);
+}
+
+int verify_callback_reject_selfSigned(int ok, X509_STORE_CTX *store)
+{
+    return verify_callback(ok, store, false);
+}
+
 const char* strtok__(const char* s, const char* d, StringBuffer& tok)
 {
     if(!s || !*s || !d || !*d)
@@ -1127,15 +1135,15 @@ const char* strtok__(const char* s, const char* d, StringBuffer& tok)
 class CSecureSocketContext : implements ISecureSocketContext, public CInterface
 {
 private:
-    SSL_CTX*    m_ctx;
+    SSL_CTX*    m_ctx = nullptr;
 #if (OPENSSL_VERSION_NUMBER > 0x00909000L) 
-    const SSL_METHOD* m_meth;
+    const SSL_METHOD* m_meth = nullptr;
 #else
     SSL_METHOD* m_meth;
 #endif 
 
-    bool m_verify;
-    bool m_address_match;
+    bool m_verify = false;
+    bool m_address_match = false;
     Owned<CStringSet> m_peers;
     StringAttr password;
 
@@ -1215,7 +1223,7 @@ public:
         SSL_CTX_set_mode(m_ctx, SSL_CTX_get_mode(m_ctx) | SSL_MODE_AUTO_RETRY);
     }
 
-    CSecureSocketContext(IPropertyTree* config, SecureSocketType sockettype)
+    CSecureSocketContext(const IPropertyTree* config, SecureSocketType sockettype)
     {
         assertex(config);
         m_verify = false;
@@ -1281,7 +1289,6 @@ public:
 
         m_verify = config->getPropBool("verify/@enable");
         m_address_match = config->getPropBool("verify/@address_match");
-        accept_selfsigned = config->getPropBool("verify/@accept_selfsigned");
 
         if(m_verify)
         {
@@ -1294,7 +1301,8 @@ public:
                 }
             }
 
-            SSL_CTX_set_verify(m_ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT | SSL_VERIFY_CLIENT_ONCE, verify_callback);
+            bool acceptSelfSigned = config->getPropBool("verify/@accept_selfsigned");
+            SSL_CTX_set_verify(m_ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT | SSL_VERIFY_CLIENT_ONCE, (acceptSelfSigned) ? verify_callback_allow_selfSigned : verify_callback_reject_selfSigned);
 
             m_peers.setown(new CStringSet());
             const char* peersstr = config->queryProp("verify/trusted_peers");
@@ -1836,7 +1844,7 @@ SECURESOCKET_API ISecureSocketContext* createSecureSocketContextEx(const char* c
     return new securesocket::CSecureSocketContext(certfile, privkeyfile, passphrase, sockettype);
 }
 
-SECURESOCKET_API ISecureSocketContext* createSecureSocketContextEx2(IPropertyTree* config, SecureSocketType sockettype)
+SECURESOCKET_API ISecureSocketContext* createSecureSocketContextEx2(const IPropertyTree* config, SecureSocketType sockettype)
 {
     if (config == NULL)
         return createSecureSocketContext(sockettype);
@@ -1844,6 +1852,14 @@ SECURESOCKET_API ISecureSocketContext* createSecureSocketContextEx2(IPropertyTre
     return new securesocket::CSecureSocketContext(config, sockettype);
 }       
 
+SECURESOCKET_API ISecureSocketContext* createSecureSocketContextSSF(ISmartSocketFactory* ssf)
+{
+    if (ssf == nullptr || !ssf->queryTlsConfig())
+        return createSecureSocketContext(ClientSocket);
+
+    return new securesocket::CSecureSocketContext(ssf->queryTlsConfig(), ClientSocket);
+}
+
 SECURESOCKET_API ISecureSocketContext* createSecureSocketContextSecret(const char *mtlsSecretName, SecureSocketType sockettype)
 {
     IPropertyTree *info = queryTlsSecretInfo(mtlsSecretName);
@@ -1974,6 +1990,11 @@ public:
         secureContext.setown(createSecureSocketContext(ClientSocket));
     }
 
+    CSecureSmartSocketFactory(IPropertyTree &service, bool _retry, unsigned _retryInterval, unsigned _dnsInterval) : CSmartSocketFactory(service, _retry, _retryInterval, _dnsInterval)
+    {
+        secureContext.setown(createSecureSocketContextEx2(queryTlsConfig(), ClientSocket));
+    }
+
     virtual ISmartSocket *connect_timeout(unsigned timeoutms) override
     {
         SocketEndpoint ep;
@@ -2002,6 +2023,11 @@ ISmartSocketFactory *createSecureSmartSocketFactory(const char *_socklist, bool
     return new CSecureSmartSocketFactory(_socklist, _retry, _retryInterval, _dnsInterval);
 }
 
+ISmartSocketFactory *createSecureSmartSocketFactory(IPropertyTree &service, bool _retry, unsigned _retryInterval, unsigned _dnsInterval)
+{
+    return new CSecureSmartSocketFactory(service, _retry, _retryInterval, _dnsInterval);
+}
+
 class CSingletonSecureSocketConnection: public CSingletonSocketConnection
 {
 public:

+ 4 - 1
system/security/securesocket/securesocket.hpp

@@ -87,14 +87,17 @@ extern "C" {
 
 SECURESOCKET_API ISecureSocketContext* createSecureSocketContext(SecureSocketType);
 SECURESOCKET_API ISecureSocketContext* createSecureSocketContextEx(const char* certfile, const char* privkeyfile, const char* passphrase, SecureSocketType);
-SECURESOCKET_API ISecureSocketContext* createSecureSocketContextEx2(IPropertyTree* config, SecureSocketType);
+SECURESOCKET_API ISecureSocketContext* createSecureSocketContextEx2(const IPropertyTree* config, SecureSocketType);
+SECURESOCKET_API ISecureSocketContext* createSecureSocketContextSSF(ISmartSocketFactory* ssf);
 SECURESOCKET_API ISecureSocketContext* createSecureSocketContextSecret(const char *mtlsSecretName, SecureSocketType);
 SECURESOCKET_API ISecureSocketContext* createSecureSocketContextSecretSrv(const char *mtlsSecretName);
 SECURESOCKET_API ICertificate *createCertificate();
 SECURESOCKET_API int signCertificate(const char* csr, const char* ca_certificate, const char* ca_privkey, const char* ca_passphrase, int days, StringBuffer& certificate);
 };
 
+
 SECURESOCKET_API ISmartSocketFactory *createSecureSmartSocketFactory(const char *_socklist, bool _retry = false, unsigned _retryInterval = 60, unsigned _dnsInterval = (unsigned) -1);
+SECURESOCKET_API ISmartSocketFactory *createSecureSmartSocketFactory(IPropertyTree &service, bool _retry = false, unsigned _retryInterval = 60, unsigned _dnsInterval = (unsigned) -1);
 
 SECURESOCKET_API IConversation *createSingletonSecureSocketConnection(unsigned short port,SocketEndpoint *_ep=nullptr);