Explorar o código

Merge pull request #14587 from afishbeck/certManager6

HPCC-25319 Support using cert-manager to generate PKI certificates

Reviewed-By: Gavin Halliday <gavin.halliday@lexisnexis.com>
Reviewed-By: Richard Chapman <rchapman@hpccsystems.com>
Richard Chapman %!s(int64=4) %!d(string=hai) anos
pai
achega
ad011fc9da
Modificáronse 41 ficheiros con 1321 adicións e 88 borrados
  1. 12 2
      common/thorhelper/thorsoapcall.cpp
  2. 10 11
      esp/applications/eclqueries/esp.yaml
  3. 11 11
      esp/applications/eclservices/esp.yaml
  4. 10 11
      esp/applications/eclwatch/esp.yaml
  5. 10 11
      esp/applications/esdl-sandbox/esp.yaml
  6. 10 11
      esp/applications/esdl/esp.yaml
  7. 10 11
      esp/applications/sql2ecl/esp.yaml
  8. 2 0
      esp/bindings/SOAP/Platform/soapbind.cpp
  9. 4 0
      esp/bindings/SOAP/Platform/soapbind.hpp
  10. 1 1
      esp/bindings/SOAP/client/soapclient.cpp
  11. 12 0
      esp/bindings/SOAP/client/soapclient.hpp
  12. 25 4
      esp/bindings/http/client/httpclient.cpp
  13. 4 0
      esp/bindings/http/client/httpclient.hpp
  14. 6 0
      esp/bindings/http/client/httpclient.ipp
  15. 2 0
      esp/scm/esp.ecm
  16. 359 0
      helm/examples/certmanager/README-vault-pki.md
  17. 200 0
      helm/examples/certmanager/README.md
  18. 18 0
      helm/examples/certmanager/ca-req.cfg
  19. 1 0
      helm/examples/certmanager/create_ca_secret.sh
  20. 14 0
      helm/examples/certmanager/eclwatch-req.cfg
  21. 1 0
      helm/examples/certmanager/gen_ca_cert_openssl.sh
  22. 15 0
      helm/examples/certmanager/localhttpcall.ecl
  23. 7 0
      helm/examples/certmanager/values-selfsigned.yaml
  24. 33 0
      helm/examples/certmanager/values-vault-pki.yaml
  25. 198 0
      helm/hpcc/templates/_helpers.tpl
  26. 4 0
      helm/hpcc/templates/dali.yaml
  27. 7 0
      helm/hpcc/templates/eclagent.yaml
  28. 7 0
      helm/hpcc/templates/eclccserver.yaml
  29. 25 0
      helm/hpcc/templates/esp.yaml
  30. 31 0
      helm/hpcc/templates/issuers.yaml
  31. 11 1
      helm/hpcc/templates/localroxie.yaml
  32. 33 4
      helm/hpcc/templates/roxie.yaml
  33. 24 0
      helm/hpcc/templates/thor.yaml
  34. 41 0
      helm/hpcc/values.schema.json
  35. 48 9
      helm/hpcc/values.yaml
  36. 23 1
      plugins/fileservices/fileservices.cpp
  37. 1 0
      roxie/ccd/ccdmain.cpp
  38. 75 0
      system/jlib/jsecrets.cpp
  39. 2 0
      system/jlib/jsecrets.hpp
  40. 12 0
      system/security/securesocket/securesocket.cpp
  41. 2 0
      system/security/securesocket/securesocket.hpp

+ 12 - 2
common/thorhelper/thorsoapcall.cpp

@@ -826,6 +826,7 @@ private:
     CTimeMon timeLimitMon;
     bool complete, timeLimitExceeded;
     bool customClientCert = false;
+    bool localClientCert = false;
     IRoxieAbortMonitor * roxieAbortMonitor;
 
 protected:
@@ -954,12 +955,19 @@ public:
         StringBuffer proxyAddress;
         proxyAddress.set(s.setown(helper->getProxyAddress()));
 
-        OwnedRoxieString hosts(helper->getHosts());
+        OwnedRoxieString hostsString(helper->getHosts());
+        const char *hosts = hostsString.get();
+
         if (isEmptyString(hosts))
             throw MakeStringException(0, "%sCALL specified no URLs",wscType == STsoap ? "SOAP" : "HTTP");
+        if (0==strncmp(hosts, "mtls:", 5))
+        {
+            localClientCert = true;
+            hosts += 5;
+        }
         if (0==strncmp(hosts, "secret:", 7))
         {
-            const char *finger = hosts.get()+7;
+            const char *finger = hosts+7;
             if (isEmptyString(finger))
                 throw MakeStringException(0, "%sCALL HTTP-CONNECT SECRET specified with no name", wscType == STsoap ? "SOAP" : "HTTP");
             if (!proxyAddress.isEmpty())
@@ -1117,6 +1125,8 @@ public:
         {
             if (clientCert != NULL)
                 ownedSC.setown(createSecureSocketContextEx(clientCert->certificate, clientCert->privateKey, clientCert->passphrase, ClientSocket));
+            else if (localClientCert)
+                ownedSC.setown(createSecureSocketContextSecret("local", ClientSocket));
             else
                 ownedSC.setown(createSecureSocketContext(ClientSocket));
         }

+ 10 - 11
esp/applications/eclqueries/esp.yaml

@@ -14,14 +14,13 @@ esp:
    logDir: "-"
 
    tls_config:
-     certificate: /etc/HPCCSystems/certificates/{$instance}/server.crt
-     privatekey: /etc/HPCCSystems/certificates/{$instance}/private.key
-     cipherList:
-       verify:
-          enable: false
-          address_match: false
-          accept_selfsigned: true
-          ca_certificates:
-            - path: "ca.pem"
-          trusted_peers:
-            - anyone
+     certificate: /opt/HPCCSystems/secrets/certificates/public/tls.crt
+     privatekey: /opt/HPCCSystems/secrets/certificates/public/tls.key
+     cipherList: "ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5"
+     verify:
+       enable: false
+       address_match: false
+       accept_selfsigned: false
+       trusted_peers: [ anyone ]
+       ca_certificates:
+       - path: "/opt/HPCCSystems/secrets/certificates/public/ca.crt"

+ 11 - 11
esp/applications/eclservices/esp.yaml

@@ -14,14 +14,14 @@ esp:
    logDir: "-"
 
    tls_config:
-     certificate: /etc/HPCCSystems/certificates/{$instance}/server.crt
-     privatekey: /etc/HPCCSystems/certificates/{$instance}/private.key
-     cipherList:
-       verify:
-          enable: false
-          address_match: false
-          accept_selfsigned: true
-          ca_certificates:
-            - path: "ca.pem"
-          trusted_peers:
-            - anyone
+     certificate: /opt/HPCCSystems/secrets/certificates/local/tls.crt
+     privatekey: /opt/HPCCSystems/secrets/certificates/local/tls.key
+     cipherList: "ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5"
+     verify:
+       # verify.enable turns on mtls which requires client certificates.  This should genenerally only be enabled for internal services.
+       enable: true
+       address_match: false
+       accept_selfsigned: false
+       trusted_peers: [ anyone ]
+       ca_certificates:
+       - path: "/opt/HPCCSystems/secrets/certificates/local/ca.crt"

+ 10 - 11
esp/applications/eclwatch/esp.yaml

@@ -14,14 +14,13 @@ esp:
    logDir: "-"
 
    tls_config:
-     certificate: /etc/HPCCSystems/certificates/{$instance}/server.crt
-     privatekey: /etc/HPCCSystems/certificates/{$instance}/private.key
-     cipherList:
-       verify:
-          enable: false
-          address_match: false
-          accept_selfsigned: true
-          ca_certificates:
-            - path: "ca.pem"
-          trusted_peers:
-            - anyone
+     certificate: /opt/HPCCSystems/secrets/certificates/public/tls.crt
+     privatekey: /opt/HPCCSystems/secrets/certificates/public/tls.key
+     cipherList: "ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5"
+     verify:
+       enable: false
+       address_match: false
+       accept_selfsigned: false
+       trusted_peers: [ anyone ]
+       ca_certificates:
+       - path: "/opt/HPCCSystems/secrets/certificates/public/ca.crt"

+ 10 - 11
esp/applications/esdl-sandbox/esp.yaml

@@ -13,14 +13,13 @@ esp:
    logDir: "-"
 
    tls_config:
-     certificate: /etc/HPCCSystems/certificates/{$instance}/server.crt
-     privatekey: /etc/HPCCSystems/certificates/{$instance}/private.key
-     cipherList:
-       verify:
-          enable: false
-          address_match: false
-          accept_selfsigned: true
-          ca_certificates:
-            - path: "ca.pem"
-          trusted_peers:
-            - anyone
+     certificate: /opt/HPCCSystems/secrets/certificates/public/tls.crt
+     privatekey: /opt/HPCCSystems/secrets/certificates/public/tls.key
+     cipherList: "ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5"
+     verify:
+       enable: false
+       address_match: false
+       accept_selfsigned: false
+       trusted_peers: [ anyone ]
+       ca_certificates:
+       - path: "/opt/HPCCSystems/secrets/certificates/public/ca.crt"

+ 10 - 11
esp/applications/esdl/esp.yaml

@@ -14,14 +14,13 @@ esp:
    logDir: "-"
 
    tls_config:
-     certificate: /etc/HPCCSystems/certificates/{$instance}/server.crt
-     privatekey: /etc/HPCCSystems/certificates/{$instance}/private.key
-     cipherList:
-       verify:
-          enable: false
-          address_match: false
-          accept_selfsigned: true
-          ca_certificates:
-            - path: "ca.pem"
-          trusted_peers:
-            - anyone
+     certificate: /opt/HPCCSystems/secrets/certificates/public/tls.crt
+     privatekey: /opt/HPCCSystems/secrets/certificates/public/tls.key
+     cipherList: "ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5"
+     verify:
+       enable: false
+       address_match: false
+       accept_selfsigned: false
+       trusted_peers: [ anyone ]
+       ca_certificates:
+       - path: "/opt/HPCCSystems/secrets/certificates/public/ca.crt"

+ 10 - 11
esp/applications/sql2ecl/esp.yaml

@@ -14,14 +14,13 @@ esp:
    logDir: "-"
 
    tls_config:
-     certificate: /etc/HPCCSystems/certificates/{$instance}/server.crt
-     privatekey: /etc/HPCCSystems/certificates/{$instance}/private.key
-     cipherList:
-       verify:
-          enable: false
-          address_match: false
-          accept_selfsigned: true
-          ca_certificates:
-            - path: "ca.pem"
-          trusted_peers:
-            - anyone
+     certificate: /opt/HPCCSystems/secrets/certificates/public/tls.crt
+     privatekey: /opt/HPCCSystems/secrets/certificates/public/tls.key
+     cipherList: "ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5"
+     verify:
+       enable: false
+       address_match: false
+       accept_selfsigned: false
+       trusted_peers: [ anyone ]
+       ca_certificates:
+       - path: "/opt/HPCCSystems/secrets/certificates/public/ca.crt"

+ 2 - 0
esp/bindings/SOAP/Platform/soapbind.cpp

@@ -271,6 +271,8 @@ void CSoapRequestBinding::post(const char *proxy, const char* url, IRpcResponseB
         soapclient.setConnectTimeoutMs(connectTimeoutMs_);
     if (readTimeoutSecs_)
         soapclient.setReadTimeoutSecs(readTimeoutSecs_);
+    if (mtls_secret_.length())
+        soapclient.setMtlsSecretName(mtls_secret_);
 
     soapclient.setUsernameToken(soap_getUserId(), soap_getPassword(), soap_getRealm());
 

+ 4 - 0
esp/bindings/SOAP/Platform/soapbind.hpp

@@ -131,6 +131,7 @@ private:
     StringBuffer userid_;
     StringBuffer password_;
     StringBuffer realm_;
+    StringBuffer mtls_secret_;
     unsigned connectTimeoutMs_ = 0;
     unsigned readTimeoutSecs_ = 0;
 
@@ -172,6 +173,9 @@ public:
     void soap_setRealm(const char *realm){realm_.clear().append(realm);}
     const char * soap_getRealm(){return realm_.str();}
 
+    void setMtlsSecretName(const char *name){mtls_secret_.set(name);}
+    const char *getMtlsSecretName(){return mtls_secret_.str();}
+
     void post(const char *proxy, const char* url, IRpcResponseBinding& response, const char *action=NULL);
 
     void post(IRpcResponseBinding& response)

+ 1 - 1
esp/bindings/SOAP/client/soapclient.cpp

@@ -148,7 +148,7 @@ int CSoapClient::postRequest(const char* contenttype, const char* soapaction, IR
     //Send request and get response
     if(m_transportclient.get() == NULL)
     {
-        Owned<IHttpClientContext> httpctx = getHttpClientContext();
+        Owned<IHttpClientContext> httpctx = getHttpClientSecretContext(m_mtls_secret);
         Owned<IHttpClient> httpclient = httpctx->createHttpClient(call->getProxy(), call->get_url());
         if (m_disableKeepAlive)
             httpclient->disableKeepAlive();

+ 12 - 0
esp/bindings/SOAP/client/soapclient.hpp

@@ -35,6 +35,7 @@ private:
     StringAttr                  m_username;
     StringAttr                  m_password;
     StringAttr                  m_realm;
+    StringAttr                  m_mtls_secret;
     bool                        m_disableKeepAlive;
     Owned<ITransportClient> m_transportclient;
     unsigned m_connectTimeoutMs = 0;
@@ -63,6 +64,17 @@ public:
             static_cast<IHttpClient*>(m_transportclient.get())->setTimeOut(m_readTimeoutSecs); //until we support something other than soap over http, static_cast
     }
 
+    void setMtlsSecretName(const char *name)
+    {
+        m_mtls_secret.set(name);
+        if (m_transportclient)
+            static_cast<IHttpClient*>(m_transportclient.get())->setMtlsSecretName(m_mtls_secret); //until we support something other than soap over http, static_cast
+    }
+    const char *getMtlsSecretName()
+    {
+        return m_mtls_secret.str();
+    }
+
     virtual int setUsernameToken(const char* userid, const char* password, const char* realm);
     virtual void disableKeepAlive() { m_disableKeepAlive = true;}
 

+ 25 - 4
esp/bindings/http/client/httpclient.cpp

@@ -30,6 +30,7 @@
 #include "SOAP/Platform/soapmessage.hpp"
 
 static Owned<CHttpClientContext> theHttpClientContext;
+static MapStringToMyClass<CHttpClientContext> httpClientContextsUsingSecrets;
 static CriticalSection httpCrit;
 
 MODULE_INIT(INIT_PRIORITY_STANDARD)
@@ -40,6 +41,7 @@ MODULE_INIT(INIT_PRIORITY_STANDARD)
 MODULE_EXIT()
 {
     theHttpClientContext.clear();
+    httpClientContextsUsingSecrets.kill();
 }
 
 /*************************************************************************
@@ -101,12 +103,12 @@ IHttpClient* CHttpClientContext::createHttpClient(const char* proxy, const char*
 
             if(m_config.get() == NULL)
             {
-                createSecureSocketContext_t xproc = NULL;
-                xproc = (createSecureSocketContext_t) pplg->getProcAddress("createSecureSocketContext");
+                createSecureSocketContextSecret_t xproc = NULL;
+                xproc = (createSecureSocketContextSecret_t) pplg->getProcAddress("createSecureSocketContextSecret");
                 if (xproc)
-                    m_ssctx.setown(xproc(ClientSocket));
+                    m_ssctx.setown(xproc(m_mtls_secret.str(), ClientSocket));
                 else
-                    throw MakeStringException(-1, "procedure createSecureSocketContext can't be loaded");
+                    throw MakeStringException(-1, "procedure createSecureSocketContextSecret can't be loaded");
             }
             else
             {
@@ -1048,6 +1050,25 @@ IHttpClientContext* getHttpClientContext()
     return theHttpClientContext.getLink();
 }
 
+IHttpClientContext* getHttpClientSecretContext(const char *secret)
+{
+    if (isEmptyString(secret))
+        return getHttpClientContext();
+    else
+    {
+        CriticalBlock b(httpCrit);
+        CHttpClientContext *ctx = httpClientContextsUsingSecrets.getValue(secret);
+        if(ctx == NULL)
+        {
+            Owned<CHttpClientContext> newctx = new CHttpClientContext();
+            newctx->setMtlsSecretName(secret);
+            httpClientContextsUsingSecrets.setValue(secret, newctx.getLink());
+            return newctx.getClear();
+        }
+        return LINK(ctx);
+    }
+}
+
 IHttpClientContext* createHttpClientContext(IPropertyTree* config)
 {
     return new CHttpClientContext(config);

+ 4 - 0
esp/bindings/http/client/httpclient.hpp

@@ -36,6 +36,9 @@ interface IHttpClient : extends ITransportClient
     virtual void setUserID(const char* userid) = 0;
     virtual void setPassword(const char* password) = 0;
     virtual void setRealm(const char* realm) = 0;
+    virtual void setMtlsSecretName(const char *name) = 0;
+    virtual const char *getMtlsSecretName() = 0;
+
     virtual void setConnectTimeOutMs(unsigned timeout) = 0;
     virtual void setTimeOut(unsigned int timeout) = 0;
     virtual void disableKeepAlive() = 0;
@@ -59,5 +62,6 @@ interface IHttpClientContext : extends IInterface
 
 esp_http_decl IHttpClientContext* getHttpClientContext();
 esp_http_decl IHttpClientContext* createHttpClientContext(IPropertyTree* config);
+esp_http_decl IHttpClientContext* getHttpClientSecretContext(const char *secret);
 
 #endif

+ 6 - 0
esp/bindings/http/client/httpclient.ipp

@@ -33,6 +33,8 @@ private:
     Owned<IPropertyTree> m_config;
     CriticalSection m_sscrit;
     Owned<IPersistentHandler> m_persistentHandler;
+    StringAttr m_mtls_secret;
+
     void initPersistentHandler();
 
 #ifdef COOKIE_HANDLING
@@ -73,6 +75,7 @@ public:
     CHttpClientContext(IPropertyTree* config);
     virtual ~CHttpClientContext();
     virtual IHttpClient* createHttpClient(const char* proxy, const char* url);
+    void setMtlsSecretName(const char *name){m_mtls_secret.set(name);}
 };
 
 class CHttpClient : implements IHttpClient, public CInterface
@@ -126,6 +129,9 @@ public:
     virtual ~CHttpClient();
     virtual void setSsCtx(ISecureSocketContext* ctx);
     virtual void disableKeepAlive() { m_disableKeepAlive = true; }
+    virtual void setMtlsSecretName(const char *name){}
+    virtual const char *getMtlsSecretName(){return nullptr;}
+
 
     virtual int sendRequest(const char* method, const char* contenttype, StringBuffer& request, StringBuffer& response);
     virtual int sendRequest(const char* method, const char* contenttype, StringBuffer& request, StringBuffer& response, StringBuffer& responseStatus, bool alwaysReadContent = false);

+ 2 - 0
esp/scm/esp.ecm

@@ -310,6 +310,8 @@ SCMinterface IEspClientRpcSettings(IEspStruct)
 
     void setReadTimeOutSecs(unsigned val);
     unsigned getReadTimeOutSecs();
+    void setMtlsSecretName(const char *name);
+    const char *getMtlsSecretName();
 };
 
 SCMinterface IEspResponse(IEspStruct)

+ 359 - 0
helm/examples/certmanager/README-vault-pki.md

@@ -0,0 +1,359 @@
+# Install Hashicorp Vault
+
+See also https://learn.hashicorp.com/tutorials/vault/kubernetes-cert-manager for a more Vault centric tutorial on setting up cert-manager with vault.
+
+## Add hashicorp to you helm repo
+```bash
+helm repo add hashicorp https://helm.releases.hashicorp.com
+```
+
+## Helm install hashicorp vault
+
+Disable the vault sidecar injector by setting "injector.enabled=false".
+
+```bash
+helm install vault hashicorp/vault --set "injector.enabled=false"
+```
+
+Check the pods:
+```bash
+kubectl get pods
+```
+
+Vault pods should be running, but not ready
+
+```bash
+$ kubectl get pods
+NAME                                       READY   STATUS    RESTARTS   AGE
+vault-0                                    0/1     Running   0          6s
+```
+
+## Initialize and unseal the vault
+
+Initialize Vault with one key share and one key threshold.  Saving off the output in json format so
+we can utilize the unseal key and root token later.
+
+```bash
+kubectl exec vault-0 -- vault operator init -key-shares=1 -key-threshold=1 -format=json > init-keys.json
+```
+
+View the unseal key found in init-keys.json.
+
+```bash
+cat init-keys.json | jq -r ".unseal_keys_b64[]"
+```
+
+Create an environment variable holding the unseal key:
+
+```bash
+VAULT_UNSEAL_KEY=$(cat init-keys.json | jq -r ".unseal_keys_b64[]")
+```
+
+Unseal Vault running on the vault-0 pod with the $VAULT_UNSEAL_KEY.
+
+```bash
+kubectl exec vault-0 -- vault operator unseal $VAULT_UNSEAL_KEY
+```
+
+Check the pods:
+```bash
+kubectl get pods
+```
+
+Vault pods should now be running and ready.
+
+## Configure the Vault PKI secrets engine (certificate authority)
+
+View the vault root token:
+```bash
+cat init-keys.json | jq -r ".root_token"
+```
+
+Create a variable named VAULT_ROOT_TOKEN to capture the root token.
+```bash
+VAULT_ROOT_TOKEN=$(cat init-keys.json | jq -r ".root_token")
+```
+
+Login to Vault running on the vault-0 pod with the $VAULT_ROOT_TOKEN.
+```bash
+kubectl exec vault-0 -- vault login $VAULT_ROOT_TOKEN
+```
+
+Start an interactive shell session on the vault-0 pod.
+```bash
+kubectl exec --stdin=true --tty=true vault-0 -- /bin/sh
+```
+We are now working from the vault-0 pod.  You should see a prompt, something like:
+
+```bash
+/ $
+```
+
+Enable the PKI secrets engine at its default path.
+```bash
+vault secrets enable pki
+```
+
+Configure the max lease time-to-live (TTL) to 8760h.
+```bash
+vault secrets tune -max-lease-ttl=8760h pki
+```
+
+# Vault CA key pair
+
+Vault can accept an existing key pair, or it can generate its own self-signed root. In general, they recommend maintaining your root CA outside of Vault and providing Vault a signed intermediate CA, but for this demo we will keep it simple and generate a self signed root certificate.
+
+Generate a self-signed certificate valid for 8760h.
+```bash
+vault write pki/root/generate/internal common_name=example.com ttl=8760h
+```
+
+Configure the PKI secrets engine certificate issuing and certificate revocation list (CRL) endpoints to use the Vault service in the default namespace.
+```bash
+vault write pki/config/urls issuing_certificates="http://vault.default:8200/v1/pki/ca" crl_distribution_points="http://vault.default:8200/v1/pki/crl"
+```
+
+For our local MTLS certificates we will use our kubernetes namespace as our domain name. This will allow us to recongize where these components reside.
+For our public TLS certificates for this demo we will use myhpcc.com as our domain.
+
+Configure a role named hpccnamespace that enables the creation of certificates hpccnamespace domain with any subdomains.
+
+```bash
+vault write pki/roles/hpcclocal key_type=any allowed_domains=default allow_subdomains=true allowed_uri_sans="spiffe://*" max_ttl=72h
+```
+
+Configure a role named myhpcc-dot-com that enables the creation of certificates myhpcc.com domain with any subdomains.
+
+```bash
+vault write pki/roles/myhpcc-dot-com allowed_domains=myhpcc.com allow_subdomains=true allowed_uri_sans="spiffe://*" max_ttl=72h
+```
+
+Create a policy named pki that enables read access to the PKI secrets engine paths.
+
+```bash
+vault policy write pki - <<EOF
+path "pki*"                        { capabilities = ["read", "list"] }
+path "pki/roles/myhpcc-dot-com"   { capabilities = ["create", "update"] }
+path "pki/sign/myhpcc-dot-com"    { capabilities = ["create", "update"] }
+path "pki/issue/myhpcc-dot-com"   { capabilities = ["create"] }
+path "pki/roles/hpcclocal"   { capabilities = ["create", "update"] }
+path "pki/sign/hpcclocal"    { capabilities = ["create", "update"] }
+path "pki/issue/hpcclocal"   { capabilities = ["create"] }
+EOF
+```
+
+Configure Kubernetes authentication
+Vault provides a Kubernetes authentication method that enables clients to authenticate with a Kubernetes Service Account Token.
+
+Enable the Kubernetes authentication method.
+
+```bash
+vault auth enable kubernetes
+```
+
+Configure the Kubernetes authentication method to use the service account token, the location of the Kubernetes host, and its certificate.
+
+```bash
+vault write auth/kubernetes/config \
+    token_reviewer_jwt="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \
+    kubernetes_host="https://$KUBERNETES_PORT_443_TCP_ADDR:443" \
+    kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
+```
+
+Finally, create a Kubernetes authentication role named issuer that binds the pki policy with a Kubernetes service account named issuer.
+
+```bash
+vault write auth/kubernetes/role/issuer \
+    bound_service_account_names=issuer \
+    bound_service_account_namespaces=cert-manager,default \
+    policies=pki \
+    ttl=20m
+```
+Exit from the vault pod:
+
+```bash
+exit
+```
+
+Deploy Cert Manager
+
+Configure an issuer and generate a certificate
+The cert-manager enables you to define Issuers that interface with the Vault certificate generating endpoints. These Issuers are invoked when a Certificate is created.
+
+Create a namespace named cert-manager to host the cert-manager.
+
+```bash
+kubectl create namespace cert-manager
+```
+
+## Install cert-manager custom resource defintions:
+
+This adds new custom resource types to kubernetes for certificate issuers and certificates.
+
+```bash
+kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.1.0/cert-manager.crds.yaml
+```
+
+## Install cert-manager helm chart:
+
+Add Jetstack helm repo:
+
+```bash
+helm repo add jetstack https://charts.jetstack.io
+```
+
+Install cert-manager.
+
+```bash
+helm install cert-manager jetstack/cert-manager --namespace cert-manager --version v1.1.0
+```
+
+Create a service account named issuer within the default namespace.
+
+```bash
+kubectl create serviceaccount issuer
+```
+
+The service account generated a secret that is required by the Issuer.
+
+Get all the secrets in the default namespace.
+
+```bash
+kubectl get secrets
+```
+
+Create a variable named ISSUER_SECRET_REF to capture the secret name.
+
+```bash
+ISSUER_SECRET_REF=$(kubectl get serviceaccount issuer -o json | jq -r ".secrets[].name")
+```
+
+## Installing the HPCC with certificate generation enabled
+
+Install the HPCC helm chart with the "--set certificates.enabled" option set to true.
+
+```bash
+helm install myhpcc hpcc/ --set global.image.version=latest --set certificates.enabled=true --set certificates.issuers.local.spec.vault.auth.kubernetes.secretRef.name=$ISSUER_SECRET_REF  --set certificates.issuers.public.spec.vault.auth.kubernetes.secretRef.name=$ISSUER_SECRET_REF --values examples/certmanager/values-vault-pki.yaml
+```
+
+Use kubectl to check the status of the deployed pods.  Wait until all pods are running before continuing.
+
+```bash
+kubectl get pods
+```
+
+Check and see if the cerficate issuers have been successfully created.
+
+```bash
+kubectl get issuers -o wide
+```
+
+You should see something like this:
+
+```bash
+NAME                 READY   STATUS           AGE
+hpcc-local-issuer    True    Vault verified   78s
+hpcc-public-issuer   True    Vault verified   78s
+```
+
+Check and see if the cerficates have been successfully created.
+
+```bash
+kubectl get certificates
+```
+
+You should see something like this:
+
+```bash
+NAME                                      READY   SECRET                                   AGE
+compile-local-myeclccserver-cert          True    compile-local-myeclccserver-tls          85s
+dali-local-mydali-cert                    True    dali-local-mydali-tls                    85s
+eclagent-local-hthor-cert                 True    eclagent-local-hthor-tls                 85s
+eclagent-local-roxie-workunit-cert        True    eclagent-local-roxie-workunit-tls        85s
+eclagent-local-thor-cert                  True    eclagent-local-thor-tls                  85s
+eclagent-local-thor-eclagent-cert         True    eclagent-local-thor-eclagent-tls         85s
+eclccserver-local-myeclccserver-cert      True    eclccserver-local-myeclccserver-tls      85s
+eclqueries-public-eclqueries-cert         True    eclqueries-public-eclqueries-tls         85s
+eclservices-local-eclservices-cert        True    eclservices-local-eclservices-tls        85s
+eclwatch-public-eclwatch-cert             True    eclwatch-public-eclwatch-tls             85s
+esdl-sandbox-public-esdl-sandbox-cert     True    esdl-sandbox-public-esdl-sandbox-tls     85s
+hthor-local-hthor-cert                    True    hthor-local-hthor-tls                    85s
+roxie-agent-public-roxie-agent-1-cert     True    roxie-agent-public-roxie-agent-1-tls     85s
+roxie-agent-public-roxie-agent-2-cert     True    roxie-agent-public-roxie-agent-2-tls     85s
+roxie-agent-local-roxie-agent-1-cert      True    roxie-agent-local-roxie-agent-1-tls      85s
+roxie-agent-local-roxie-agent-2-cert      True    roxie-agent-local-roxie-agent-2-tls      85s
+roxie-local-roxie-workunit-cert           True    roxie-local-roxie-workunit-tls           85s
+sql2ecl-public-sql2ecl-cert               True    sql2ecl-public-sql2ecl-tls               85s
+thoragent-local-thor-thoragent-cert       True    thoragent-local-thor-thoragent-tls       85s
+thormanager-local-thormanager-w-cert      True    thormanager-local-thormanager-w-tls      85s
+thorworker-local-thorworker-w-cert        True    thorworker-local-thorworker-w-tls        85s
+topo-local-roxie-toposerver-cert          True    topo-local-roxie-toposerver-tls          85s
+udpkey-udp-roxie-cert                     True    udpkey-udp-roxie-dtls                    85s
+```
+
+List the kubernetes secrets, which now include the generated tls secrets.
+
+```bash
+kubectl get secrets
+```
+
+You should see something like this:
+
+```bash
+NAME                                     TYPE                                  DATA   AGE
+cert-manager-cainjector-token-wmdxq      kubernetes.io/service-account-token   3      3m52s
+cert-manager-token-jtlgx                 kubernetes.io/service-account-token   3      3m52s
+cert-manager-webhook-ca                  Opaque                                3      3m51s
+cert-manager-webhook-token-df4xk         kubernetes.io/service-account-token   3      3m52s
+compile-local-myeclccserver-tls          kubernetes.io/tls                     3      2m49s
+dali-local-mydali-tls                    kubernetes.io/tls                     3      2m56s
+default-token-kmj97                      kubernetes.io/service-account-token   3      2d1h
+eclagent-local-hthor-tls                 kubernetes.io/tls                     3      2m55s
+eclagent-local-roxie-workunit-tls        kubernetes.io/tls                     3      2m53s
+eclagent-local-thor-eclagent-tls         kubernetes.io/tls                     3      2m56s
+eclagent-local-thor-tls                  kubernetes.io/tls                     3      2m54s
+eclccserver-local-myeclccserver-tls      kubernetes.io/tls                     3      2m55s
+eclqueries-public-eclqueries-tls         kubernetes.io/tls                     3      2m52s
+eclservices-local-eclservices-tls        kubernetes.io/tls                     3      2m54s
+eclwatch-public-eclwatch-tls             kubernetes.io/tls                     3      2m50s
+esdl-sandbox-public-esdl-sandbox-tls     kubernetes.io/tls                     3      2m49s
+hpcc-agent-token-h78cd                   kubernetes.io/service-account-token   3      2m58s
+hpcc-default-token-55lss                 kubernetes.io/service-account-token   3      2m58s
+hpcc-local-issuer-key-pair               kubernetes.io/tls                     2      3m23s
+hpcc-thoragent-token-xkm7j               kubernetes.io/service-account-token   3      2m58s
+hthor-local-hthor-tls                    kubernetes.io/tls                     3      2m49s
+myhpcc-filebeat-token-vjplq              kubernetes.io/service-account-token   3      2m58s
+roxie-agent-public-roxie-agent-1-tls     kubernetes.io/tls                     3      2m51s
+roxie-agent-public-roxie-agent-2-tls     kubernetes.io/tls                     3      2m49s
+roxie-agent-local-roxie-agent-1-tls      kubernetes.io/tls                     3      2m51s
+roxie-agent-local-roxie-agent-2-tls      kubernetes.io/tls                     3      2m52s
+roxie-local-roxie-workunit-tls           kubernetes.io/tls                     3      2m52s
+sh.helm.release.v1.cert-manager.v1       helm.sh/release.v1                    1      3m52s
+sh.helm.release.v1.myhpcc.v1             helm.sh/release.v1                    1      2m58s
+sql2ecl-public-sql2ecl-tls               kubernetes.io/tls                     3      2m55s
+thoragent-local-thor-thoragent-tls       kubernetes.io/tls                     3      2m52s
+thormanager-local-thormanager-w-tls      kubernetes.io/tls                     3      2m51s
+thorworker-local-thorworker-w-tls        kubernetes.io/tls                     3      2m51s
+topo-local-roxie-toposerver-tls          kubernetes.io/tls                     3      2m53s
+udpkey-udp-roxie-dtls                    kubernetes.io/tls                     3      2m55s
+```
+
+The cluster ESPs are now using TLS both locally and publicly.
+
+Run an ecl job that requires using mutual TLS (using local client certificate):
+
+```
+ecl run --ssl hthor examples/certmanager/localhttpcall.ecl
+```
+
+Note that for the HTTPCALL in our ecl example the url now starts with "mtls:" this tells HTTPCALL/SOAPCALL to use mutual TLS, using the local client certificate, and to verify the server using the local certificate authority certificate.
+
+You should see a result similar to this:
+
+```xml
+<Result>
+<Dataset name='localHttpEchoResult'>
+ <Row><Method>GET</Method><UrlPath>/WsSmc/HttpEcho</UrlPath><UrlParameters>name=doe,joe&amp;number=1</UrlParameters><Headers><Header>Accept-Encoding: gzip, deflate</Header><Header>Accept: text/xml</Header></Headers><Content></Content></Row>
+</Dataset>
+</Result>
+```

+ 200 - 0
helm/examples/certmanager/README.md

@@ -0,0 +1,200 @@
+# HPCC Systems Certificates using JetStack cert-manager
+
+This example demonstrates HPCC TLS configuration using Jetstack cert-manager.
+
+## Jetstack cert-manager support:
+
+The following will use cert-manager to automatically provision and manage TLS certificates for the
+HPCC.
+
+The following steps can be used to set up cert-manager in a kubernetes cluster.
+
+--------------------------------------------------------------------------------------------------------
+
+## Install cert-manager custom resource defintions:
+
+This adds new custom resource types to kubernetes for certificate issuers and certificates.
+
+```
+kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.1.0/cert-manager.crds.yaml
+```
+
+## Install cert-manager helm chart:
+
+Add Jetstack helm repo:
+
+```bash
+helm repo add jetstack https://charts.jetstack.io
+```
+
+Install vault server.
+
+```bash
+helm install cert-manager jetstack/cert-manager --version v1.1.0
+```
+
+## Run from HPCC-Platform/helm directory
+
+For now this example will assume you are in the helm directory of the HPCC-Systems source.
+
+## Create a root certificate for our local cluster certificate authority
+
+This example uses OpenSSL to generate the root certificate for our local cluster certificate authority.
+
+We can create a root certificate and private key for our local cluster certificate authority with
+a single openssl call. This call uses the openssl config file found in the examples directory (ca-req.cfg).
+
+
+```bash
+openssl req -x509 -newkey rsa:2048 -nodes -keyout ca.key -sha256 -days 1825 -out ca.crt -config examples/certmanager/ca-req.cfg
+```
+
+For additonal information on the openssl command being used checkout this link:
+https://www.openssl.org/docs/man1.0.2/man1/openssl-req.html
+
+For a general overview check out this link:
+https://www.golinuxcloud.com/create-certificate-authority-root-ca-linux
+
+
+## Create a Kubernetes TLS secret from the generated root certificate and privatekey
+
+The root certificate needs to be added as a kubernetes secret in order to be accessible to cert-manager.
+The secret name matches the default name used in the local issuer configuration in values.yaml.
+
+```bash
+kubectl create secret tls hpcc-local-issuer-key-pair --cert=ca.crt --key=ca.key
+```
+
+## Installing the HPCC with certificate generation enabled
+
+Install the HPCC helm chart with the "--set certificates.enabled" option set to true.
+
+```bash
+helm install myhpcc hpcc/ --set global.image.version=latest --set certificates.enabled=true
+```
+
+Use kubectl to check the status of the deployed pods.  Wait until all pods are running before continuing.
+
+```bash
+kubectl get pods
+```
+
+Check and see if the cerficate issuers have been successfully created.
+
+```bash
+kubectl get issuers -o wide
+```
+
+You should see something like this:
+
+```bash
+NAME                   READY   STATUS                AGE
+hpcc-public-issuer     True                          3m57s
+hpcc-local-issuer      True    Signing CA verified   3m57s
+```
+
+Check and see if the cerficates have been successfully created.
+
+```bash
+kubectl get certificates
+```
+
+You should see something like this:
+
+```bash
+NAME                                      READY   SECRET                                   AGE
+compile-local-myeclccserver-cert          True    compile-local-myeclccserver-tls          85s
+dali-local-mydali-cert                    True    dali-local-mydali-tls                    85s
+eclagent-local-hthor-cert                 True    eclagent-local-hthor-tls                 85s
+eclagent-local-roxie-workunit-cert        True    eclagent-local-roxie-workunit-tls        85s
+eclagent-local-thor-cert                  True    eclagent-local-thor-tls                  85s
+eclagent-local-thor-eclagent-cert         True    eclagent-local-thor-eclagent-tls         85s
+eclccserver-local-myeclccserver-cert      True    eclccserver-local-myeclccserver-tls      85s
+eclqueries-public-eclqueries-cert         True    eclqueries-public-eclqueries-tls         85s
+eclservices-local-eclservices-cert        True    eclservices-local-eclservices-tls        85s
+eclwatch-public-eclwatch-cert             True    eclwatch-public-eclwatch-tls             85s
+esdl-sandbox-public-esdl-sandbox-cert     True    esdl-sandbox-public-esdl-sandbox-tls     85s
+hthor-local-hthor-cert                    True    hthor-local-hthor-tls                    85s
+roxie-agent-public-roxie-agent-1-cert     True    roxie-agent-public-roxie-agent-1-tls     85s
+roxie-agent-public-roxie-agent-2-cert     True    roxie-agent-public-roxie-agent-2-tls     85s
+roxie-agent-local-roxie-agent-1-cert      True    roxie-agent-local-roxie-agent-1-tls      85s
+roxie-agent-local-roxie-agent-2-cert      True    roxie-agent-local-roxie-agent-2-tls      85s
+roxie-local-roxie-workunit-cert           True    roxie-local-roxie-workunit-tls           85s
+sql2ecl-public-sql2ecl-cert               True    sql2ecl-public-sql2ecl-tls               85s
+thoragent-local-thor-thoragent-cert       True    thoragent-local-thor-thoragent-tls       85s
+thormanager-local-thormanager-w-cert      True    thormanager-local-thormanager-w-tls      85s
+thorworker-local-thorworker-w-cert        True    thorworker-local-thorworker-w-tls        85s
+topo-local-roxie-toposerver-cert          True    topo-local-roxie-toposerver-tls          85s
+udpkey-udp-roxie-cert                     True    udpkey-udp-roxie-dtls                    85s
+```
+
+List the kubernetes secrets, which now include the generated tls secrets.
+
+```bash
+kubectl get secrets
+```
+
+You should see something like this:
+
+```bash
+NAME                                     TYPE                                  DATA   AGE
+cert-manager-cainjector-token-wmdxq      kubernetes.io/service-account-token   3      3m52s
+cert-manager-token-jtlgx                 kubernetes.io/service-account-token   3      3m52s
+cert-manager-webhook-ca                  Opaque                                3      3m51s
+cert-manager-webhook-token-df4xk         kubernetes.io/service-account-token   3      3m52s
+compile-local-myeclccserver-tls          kubernetes.io/tls                     3      2m49s
+dali-local-mydali-tls                    kubernetes.io/tls                     3      2m56s
+default-token-kmj97                      kubernetes.io/service-account-token   3      2d1h
+eclagent-local-hthor-tls                 kubernetes.io/tls                     3      2m55s
+eclagent-local-roxie-workunit-tls        kubernetes.io/tls                     3      2m53s
+eclagent-local-thor-eclagent-tls         kubernetes.io/tls                     3      2m56s
+eclagent-local-thor-tls                  kubernetes.io/tls                     3      2m54s
+eclccserver-local-myeclccserver-tls      kubernetes.io/tls                     3      2m55s
+eclqueries-public-eclqueries-tls         kubernetes.io/tls                     3      2m52s
+eclservices-local-eclservices-tls        kubernetes.io/tls                     3      2m54s
+eclwatch-public-eclwatch-tls             kubernetes.io/tls                     3      2m50s
+esdl-sandbox-public-esdl-sandbox-tls     kubernetes.io/tls                     3      2m49s
+hpcc-agent-token-h78cd                   kubernetes.io/service-account-token   3      2m58s
+hpcc-default-token-55lss                 kubernetes.io/service-account-token   3      2m58s
+hpcc-local-issuer-key-pair               kubernetes.io/tls                     2      3m23s
+hpcc-thoragent-token-xkm7j               kubernetes.io/service-account-token   3      2m58s
+hthor-local-hthor-tls                    kubernetes.io/tls                     3      2m49s
+myhpcc-filebeat-token-vjplq              kubernetes.io/service-account-token   3      2m58s
+roxie-agent-public-roxie-agent-1-tls     kubernetes.io/tls                     3      2m51s
+roxie-agent-public-roxie-agent-2-tls     kubernetes.io/tls                     3      2m49s
+roxie-agent-local-roxie-agent-1-tls      kubernetes.io/tls                     3      2m51s
+roxie-agent-local-roxie-agent-2-tls      kubernetes.io/tls                     3      2m52s
+roxie-local-roxie-workunit-tls           kubernetes.io/tls                     3      2m52s
+sh.helm.release.v1.cert-manager.v1       helm.sh/release.v1                    1      3m52s
+sh.helm.release.v1.myhpcc.v1             helm.sh/release.v1                    1      2m58s
+sql2ecl-public-sql2ecl-tls               kubernetes.io/tls                     3      2m55s
+thoragent-local-thor-thoragent-tls       kubernetes.io/tls                     3      2m52s
+thormanager-local-thormanager-w-tls      kubernetes.io/tls                     3      2m51s
+thorworker-local-thorworker-w-tls        kubernetes.io/tls                     3      2m51s
+topo-local-roxie-toposerver-tls          kubernetes.io/tls                     3      2m53s
+udpkey-udp-roxie-dtls                    kubernetes.io/tls                     3      2m55s
+```
+
+The cluster ESPs are now using TLS both locally and publicly.
+
+Run an ecl job that requires using mutual TLS (using local client certificate):
+
+```
+ecl run --ssl hthor examples/certmanager/localhttpcall.ecl
+```
+
+Note that for the HTTPCALL in our ecl example the url now starts with "mtls:" this tells HTTPCALL/SOAPCALL to use mutual TLS, using the local client certificate, and to verify the server using the local certificate authority certificate.
+
+You should see a result similar to this:
+
+```xml
+<Result>
+<Dataset name='localHttpEchoResult'>
+ <Row><Method>GET</Method><UrlPath>/WsSmc/HttpEcho</UrlPath><UrlParameters>name=doe,joe&amp;number=1</UrlParameters><Headers><Header>Accept-Encoding: gzip, deflate</Header><Header>Accept: text/xml</Header></Headers><Content></Content></Row>
+</Dataset>
+</Result>
+```
+
+The default public issuer uses self signed certificates. This makes it very easy to set up but browsers
+will not recognize the certificates as trustworthy and the browser will warn users that the connection
+is not safe.

+ 18 - 0
helm/examples/certmanager/ca-req.cfg

@@ -0,0 +1,18 @@
+ [req]
+ default_bits           = 2048
+ default_keyfile        = ca.key
+ distinguished_name     = dn
+ prompt                 = no
+ x509_extensions        = x509_ca
+
+ [dn]
+ C                      = US
+ ST                     = Georgia
+ L                      = Alpharetta
+ O                      = HPCC Systems
+ OU                     = HPCC Example
+ CN                     = Internal Cluster CA
+ emailAddress           = info@hpccsystems.com
+
+ [x509_ca]
+ basicConstraints=CA:true,pathlen:1

+ 1 - 0
helm/examples/certmanager/create_ca_secret.sh

@@ -0,0 +1 @@
+kubectl create secret tls hpcc-local-issuer-key-pair --cert=ca.crt --key=ca.key

+ 14 - 0
helm/examples/certmanager/eclwatch-req.cfg

@@ -0,0 +1,14 @@
+ [req]
+ default_bits           = 2048
+ default_keyfile        = eclwatch.key
+ distinguished_name     = dn
+ prompt                 = no
+
+ [dn]
+ C                      = US
+ ST                     = Georgia
+ L                      = Alpharetta
+ O                      = HPCC Systems
+ OU                     = HPCC Example
+ CN                     = eclwatch.myhpcc.com
+ emailAddress           = info@hpccsystems.com

+ 1 - 0
helm/examples/certmanager/gen_ca_cert_openssl.sh

@@ -0,0 +1 @@
+openssl req -x509 -newkey rsa:2048 -nodes -keyout ca.key -sha256 -days 1825 -out ca.crt -config ca-req.cfg

+ 15 - 0
helm/examples/certmanager/localhttpcall.ecl

@@ -0,0 +1,15 @@
+responseRecord :=
+    RECORD
+        string method{xpath('Method')};
+        string path{xpath('UrlPath')};
+        string parameters{xpath('UrlParameters')};
+        set of string headers{xpath('Headers/Header')};
+        string content{xpath('Content')};
+    END;
+
+//mtls: tells HTTPCALL/SOAPCALL to use mutual TLS, using the local client and CA certificates
+//
+string hostURL := 'mtls:https://eclservices:8010/WsSmc/HttpEcho?name=doe,joe&number=1';
+
+echoResult := HTTPCALL(hostURL,'GET', 'text/xml', responseRecord, xpath('Envelope/Body/HttpEchoResponse'));
+output(echoResult, named('localHttpEchoResult'));

+ 7 - 0
helm/examples/certmanager/values-selfsigned.yaml

@@ -0,0 +1,7 @@
+certificates:
+  issuers:
+    local:
+      spec:
+        ca: null
+        selfSigned: {}
+

+ 33 - 0
helm/examples/certmanager/values-vault-pki.yaml

@@ -0,0 +1,33 @@
+certificates:
+  issuers:
+    local:
+      spec:
+      # delete any alternative spec config, and then add vault
+        ca: null
+        selfSigned: null
+        vault:
+          server: http://vault.default:8200
+          path: pki/sign/hpcclocal
+          auth:
+            kubernetes:
+              mountPath: /v1/auth/kubernetes
+              role: issuer
+              secretRef:
+                name: tbd # requires service account secret, set from command line
+                key: token
+    public:
+      domain: myhpcc.com
+      spec:
+      # delete any alternative spec config, and then add vault
+        ca: null
+        selfSigned: null
+        vault:
+          server: http://vault.default:8200
+          path: pki/sign/myhpcc-dot-com
+          auth:
+            kubernetes:
+              mountPath: /v1/auth/kubernetes
+              role: issuer
+              secretRef:
+                name: tbd # requires service account secret, set from command line
+                key: token

+ 198 - 0
helm/hpcc/templates/_helpers.tpl

@@ -648,7 +648,11 @@ Generate list of available services
 - name: {{ $esp.name }}
   type: {{ $esp.application }}
   port: {{ $esp.servicePort }}
+  {{- if hasKey $esp "tls" }}
   tls: {{ $esp.tls }}
+  {{- else }}
+  tls: {{ ($.Values.certificates | default dict).enabled }}
+  {{- end }}
   public: {{ $esp.public }}
 {{ end -}}
 {{- range $dali := $.Values.dali -}}
@@ -948,3 +952,197 @@ args:
     {{ $postJobCommand }}
 {{- end }}
 {{- end -}}
+
+{{/*
+Use cert-manager to create a public certificate and private key for use with TLS
+There are separate certificate issuers for local and public certificates
+by default public certificates are self-signed and local certificates are signed
+by our own certificate authority.  A CA certificate is also provided to the pod
+so that we can recognize the signature of our own CA.
+*/}}
+{{- define "hpcc.addCertificate" }}
+{{- if (.root.Values.certificates | default dict).enabled -}}
+{{- $externalCert := and (hasKey . "external") .external -}}
+{{- $issuer := ternary .root.Values.certificates.issuers.public .root.Values.certificates.issuers.local $externalCert -}}
+{{- if $issuer -}}
+{{- $namespace := .root.Release.Namespace -}}
+{{- $service := (.service | default dict) -}}
+{{- $domain := ( $service.domain | default $issuer.domain | default $namespace | default "default" ) -}}
+{{- $exposure := ternary "public" "local" $externalCert -}}
+{{- $name := .name }}
+
+apiVersion: cert-manager.io/v1
+kind: Certificate
+metadata:
+  name: {{ .component }}-{{ $exposure }}-{{ $name }}-cert
+  namespace: {{ $namespace }}
+spec:
+  # Secret names are always required.
+  secretName: {{ .component }}-{{ $exposure }}-{{ $name }}-tls
+  duration: 2160h # 90d
+  renewBefore: 360h # 15d
+  subject:
+    organizations:
+    - HPCC Systems
+  commonName: {{ $name }}.{{ $domain }}
+  isCA: false
+  privateKey:
+    algorithm: RSA
+    encoding: PKCS1
+    size: 2048
+  usages:
+    - server auth
+    - client auth
+  dnsNames:
+ {{- /* if servicename is passed we simply create a service entry of that name */ -}}
+ {{- if .servicename }}
+  - {{ .servicename }}.{{ $domain }}
+ {{- /* if service parameter is passed in we are using the component config as a service config entry */ -}}
+ {{- else if .service -}}
+   {{- $public := and (hasKey .service "public") .service.public -}}
+   {{- if eq $public $externalCert }}
+  - {{ .service.name }}.{{ $domain }}
+   {{- end }}
+ {{- /* if services parameter is passed the component has an array of services to configure */ -}}
+ {{- else if .services -}}
+  {{- range $service := .services }}
+   {{- $external := and (hasKey $service "external") $service.external -}}
+   {{- if eq $external $externalCert }}
+  - {{ $service.name }}.{{ $domain }}
+   {{- end }}
+  {{- end }}
+ {{- else if not $externalCert }}
+  - "{{ $name }}.{{ $domain }}"
+ {{- end }}
+  uris:
+  - spiffe://hpcc.{{ $domain }}/{{ .component }}/{{ $name }}
+  # Issuer references are always required.
+  issuerRef:
+    name: {{ $issuer.name }}
+    # We can reference ClusterIssuers by changing the kind here.
+    kind: {{ $issuer.kind }}
+    group: cert-manager.io
+---
+{{- end }}
+{{- end }}
+{{- end }}
+
+{{/*
+Experimental: Use certmanager to generate a key for roxie udp encryption.
+A public certificate and private key are generated under /opt/HPCCSystems/secrets/certificates/udp.
+Current udp encryption design would only use the private key.
+Key is in pem format and the private key would need to be extracted.
+*/}}
+{{- define "hpcc.addUDPCertificate" }}
+{{- if (.root.Values.certificates | default dict).enabled -}}
+{{- $issuer := .root.Values.certificates.issuers.local -}}
+{{- $namespace := .root.Release.Namespace -}}
+{{- $name := .name -}}
+{{- if $issuer }}
+apiVersion: cert-manager.io/v1
+kind: Certificate
+metadata:
+  name: {{ .component }}-udp-{{ $name }}-cert
+  namespace: {{ $namespace }}
+spec:
+  # Secret names are always required.
+  secretName: {{ .component }}-udp-{{ $name }}-dtls
+  duration: 2160h # 90d
+  renewBefore: 360h # 15d
+  subject:
+    organizations:
+    - HPCC Systems
+  commonName: {{ $name }}.{{ $namespace }}
+  isCA: false
+  privateKey:
+    algorithm: ECDSA
+    encoding: PKCS1
+    size: 256
+  usages:
+    - server auth
+    - client auth
+  # At least one of a DNS Name, URI, or IP address is required.
+  uris:
+  - spiffe://hpcc.{{ $namespace }}/{{ .component }}/{{ $name }}
+  # Issuer references are always required.
+  issuerRef:
+    name: {{ $issuer.name }}
+    # We can reference ClusterIssuers by changing the kind here.
+    # The default value is Issuer (i.e. a locally namespaced Issuer)
+    kind: {{ $issuer.kind }}
+    group: cert-manager.io
+---
+{{- end }}
+{{- end }}
+{{- end }}
+
+{{/*
+Add a certficate volume mount for a component
+*/}}
+{{- define "hpcc.addCertificateVolumeMount" -}}
+{{- $externalCert := and (hasKey . "external") .external -}}
+{{- $exposure := ternary "public" "local" $externalCert }}
+{{- /*
+    A .certificate parameter means the user explictly configured a certificate to use
+    otherwise check if certificate generation is enabled
+*/ -}}
+{{- if .certificate -}}
+- name: certificate-{{ .component }}-{{ $exposure }}-{{ .name }}
+  mountPath: /opt/HPCCSystems/secrets/certificates/{{ $exposure }}
+{{- else if (.root.Values.certificates | default dict).enabled -}}
+{{- $issuer := ternary .root.Values.certificates.issuers.public .root.Values.certificates.issuers.local $externalCert -}}
+{{- if $issuer -}}
+- name: certificate-{{ .component }}-{{ $exposure }}-{{ .name }}
+  mountPath: /opt/HPCCSystems/secrets/certificates/{{ $exposure }}
+{{- end }}
+{{- end -}}
+{{- end -}}
+
+{{/*
+Add a secret volume for a certificate
+*/}}
+{{- define "hpcc.addCertificateVolume" -}}
+{{- $externalCert := and (hasKey . "external") .external -}}
+{{- $exposure := ternary "public" "local" $externalCert -}}
+{{- /*
+    A .certificate parameter means the user explictly configured a certificate to use
+    otherwise check if certificate generation is enabled
+*/ -}}
+{{- if .certificate -}}
+- name: certificate-{{ .component }}-{{ $exposure }}-{{ .name }}
+  secret:
+    secretName: {{ .certificate }}
+{{- else if (.root.Values.certificates | default dict).enabled -}}
+{{- $issuer := ternary .root.Values.certificates.issuers.public .root.Values.certificates.issuers.local $externalCert -}}
+{{- if $issuer -}}
+- name: certificate-{{ .component }}-{{ $exposure }}-{{ .name }}
+  secret:
+    secretName: {{ .component }}-{{ $exposure }}-{{ .name }}-tls
+{{- end -}}
+{{- end -}}
+{{- end -}}
+
+{{/*
+Add the certficate volume mount for a roxie udp key
+*/}}
+{{- define "hpcc.addUDPCertificateVolumeMount" }}
+{{- if (.root.Values.certificates | default dict).enabled -}}
+{{- if .root.Values.certificates.issuers.local -}}
+- name: certificate-{{ .component }}-udp-{{ .name }}
+  mountPath: /opt/HPCCSystems/secrets/certificates/udp
+{{- end -}}
+{{- end -}}
+{{- end -}}
+
+{{/*
+Add a secret volume for a roxie udp key
+*/}}
+{{- define "hpcc.addUDPCertificateVolume" }}
+{{- if (.root.Values.certificates | default dict).enabled -}}
+{{- if .root.Values.certificates.issuers.local -}}
+- name: certificate-{{ .component }}-udp-{{ .name }}
+  secret:
+    secretName: {{ .component }}-udp-{{ .name }}-dtls
+{{ end -}}
+{{- end -}}
+{{- end -}}

+ 4 - 0
helm/hpcc/templates/dali.yaml

@@ -39,6 +39,7 @@ spec:
 {{ include "hpcc.addConfigMapVolumeMount" $dali | indent 8 }}
 {{ include "hpcc.addDaliVolumeMount" $commonCtx | indent 8 }}
 {{ include "hpcc.addSecretVolumeMounts" (dict "root" $ "secretsCategories" $daliSecretsCategories) | indent 8 }}
+{{ include "hpcc.addCertificateVolumeMount" (dict "root" $ "name" .name "component" "dali" "external" false) | indent 8 }}
 {{- if not $sashaServices.disabled -}}
 {{- range $sashaName, $_sasha := $dali.services -}}
 {{- $sasha := ($_sasha | default dict) -}}
@@ -76,6 +77,7 @@ spec:
 {{ include "hpcc.addDllVolume" $commonCtx | indent 6 -}}
 {{- end -}}
 {{- include "hpcc.addSecretVolumes" (dict "root" $ "secretsCategories" $tmpDaliScope.aggregateSashaSecretsCategories) | indent 6 }}
+{{ include "hpcc.addCertificateVolume" (dict "root" $ "name" .name "component" "dali" "external" false) | indent 6 }}
 ---
 {{- $storage := ($.Values.storage | default dict) -}}
 {{- $daliStorage := ($storage.daliStorage | default dict) }}
@@ -132,6 +134,8 @@ spec:
     run: {{ .name | quote }}
   type: ClusterIP
 ---
+{{ include "hpcc.addCertificate" (dict "root" $ "name" .name "service" . "component" "dali" "external" false) }}
+
 {{- $storage := ($.Values.storage | default dict) -}}
 {{- $daliStorage := ($storage.daliStorage | default dict) -}}
 {{- if (and (not $daliStorage.existingClaim) (not $daliStorage.plane)) }}

+ 7 - 0
helm/hpcc/templates/eclagent.yaml

@@ -56,6 +56,7 @@ data:
 {{ include "hpcc.addDataVolumeMount" . | indent 12 }}
 {{ include "hpcc.addDllVolumeMount" . | indent 12 }}
 {{ include "hpcc.addSecretVolumeMounts" . | indent 12 }}
+{{ include "hpcc.addCertificateVolumeMount" (dict "root" .root "name" .me.name "component" .me.type) | indent 12 }}
 {{- if $misc.postJobCommandViaSidecar }}
 {{ include "hpcc.addWaitAndRunVolumeMount" . | indent 12 }}
 {{- end }}
@@ -64,6 +65,7 @@ data:
 {{ include "hpcc.addDataVolume" . | indent 10 }}
 {{ include "hpcc.addDllVolume" . | indent 10 }}
 {{ include "hpcc.addSecretVolumes" . | indent 10 }}
+{{ include "hpcc.addCertificateVolume" (dict "root" .root "name" .me.name "component" .me.type) | indent 10 }}
 {{- if $misc.postJobCommandViaSidecar }}
 {{ include "hpcc.addWaitAndRunVolume" . | indent 10 }}
 {{- end }}
@@ -121,14 +123,19 @@ spec:
 {{ include "hpcc.addDataVolumeMount" $commonCtx | indent 8 }}
 {{ include "hpcc.addDllVolumeMount" $commonCtx | indent 8 }}
 {{ include "hpcc.addSecretVolumeMounts" $commonCtx | indent 8 }}
+{{ include "hpcc.addCertificateVolumeMount" (dict "root" $ "name" .name "component" "eclagent") | indent 8 }}
       volumes:
 {{ include "hpcc.addConfigMapVolume" . | indent 6 }}
 {{ include "hpcc.addDataVolume" $commonCtx | indent 6 }}
 {{ include "hpcc.addDllVolume" $commonCtx | indent 6 }}
 {{ include "hpcc.addSecretVolumes" $commonCtx | indent 6 }}
+{{ include "hpcc.addCertificateVolume" (dict "root" $ "name" .name "component" "eclagent") | indent 6 }}
 ---
 kind: ConfigMap 
 {{ include "hpcc.agentConfigMap" $commonCtx }}
 ---
+{{ include "hpcc.addCertificate" (dict "root" $ "name" .name "component" "eclagent") }}
+{{ include "hpcc.addCertificate" (dict "root" $ "name" .name "component" .type) }}
+
 {{- end }}
 {{- end }}

+ 7 - 0
helm/hpcc/templates/eclccserver.yaml

@@ -57,6 +57,7 @@ data:
 {{ include "hpcc.addDataVolumeMount" . | indent 12 }}
 {{ include "hpcc.addDllVolumeMount" . | indent 12 }}
 {{ include "hpcc.addSecretVolumeMounts" . | indent 12 }}
+{{ include "hpcc.addCertificateVolumeMount" (dict "root" .root "name" .me.name "component" "compile") | indent 12 }}
 {{- if $misc.postJobCommandViaSidecar }}
 {{ include "hpcc.addWaitAndRunVolumeMount" . | indent 12 }}
 {{- end }}
@@ -65,6 +66,7 @@ data:
 {{ include "hpcc.addDataVolume" . | indent 10 }}
 {{ include "hpcc.addDllVolume" . | indent 10 }}
 {{ include "hpcc.addSecretVolumes" . | indent 10 }}
+{{ include "hpcc.addCertificateVolume" (dict "root" .root "name" .me.name "component" "compile") | indent 10 }}
 {{- if $misc.postJobCommandViaSidecar }}
 {{ include "hpcc.addWaitAndRunVolume" . | indent 10 }}
 {{- end }}
@@ -124,6 +126,7 @@ spec:
 {{ include "hpcc.addDataVolumeMount" $commonCtx | indent 8 }}
 {{ include "hpcc.addDllVolumeMount" $commonCtx | indent 8 }}
 {{ include "hpcc.addSecretVolumeMounts" $commonCtx | indent 8 }}
+{{ include "hpcc.addCertificateVolumeMount" (dict "root" $ "name" .name "component" "eclccserver") | indent 8 }}
         - name: "hpccbundles"
           mountPath: "/home/hpcc/.HPCCSystems"
       volumes:
@@ -131,6 +134,7 @@ spec:
 {{ include "hpcc.addDataVolume" $commonCtx | indent 6 }}
 {{ include "hpcc.addDllVolume" $commonCtx | indent 6 }}
 {{ include "hpcc.addSecretVolumes" $commonCtx | indent 6 }}
+{{ include "hpcc.addCertificateVolume" (dict "root" $ "name" .name "component" "eclccserver") | indent 6 }}
       - name: hpccbundles
         emptyDir: {}
 ---
@@ -138,5 +142,8 @@ kind: ConfigMap
 {{ include "hpcc.eclccServerConfigMap" $commonCtx }}
 
 ---
+{{ include "hpcc.addCertificate" (dict "root" $ "name" .name "component" "eclccserver") }}
+{{ include "hpcc.addCertificate" (dict "root" $ "name" .name "component" "compile") }}
+
 {{- end }}
 {{- end }}

+ 25 - 0
helm/hpcc/templates/esp.yaml

@@ -12,6 +12,28 @@ data:
     esp:
 {{ toYaml (omit .me "logging") | indent 6 }}
 {{- include "hpcc.generateLoggingConfig" . | indent 6 }}
+{{- if and .root.Values.certificates .root.Values.certificates.enabled }}
+ {{- if not (hasKey .me "tls" )}}
+      tls: true
+ {{- end }}
+      tls_config:
+ {{- if .me.public }}
+        certificate: /opt/HPCCSystems/secrets/certificates/public/tls.crt
+        privatekey: /opt/HPCCSystems/secrets/certificates/public/tls.key
+ {{- else }}
+        certificate: /opt/HPCCSystems/secrets/certificates/local/tls.crt
+        privatekey: /opt/HPCCSystems/secrets/certificates/local/tls.key
+        verify:
+          enable: true
+          address_match: false
+          accept_selfsigned: false
+          trusted_peers: [ anyone ]
+          ca_certificates:
+            path: "/opt/HPCCSystems/secrets/certificates/local/ca.crt"
+ {{- end }}
+{{- else if not (hasKey .me "tls" )}}
+      tls: false
+{{- end }}
       queues:
 {{ include "hpcc.generateConfigMapQueues" .root | indent 6 }}
       services:
@@ -66,11 +88,13 @@ spec:
 {{ include "hpcc.addDataVolumeMount" $commonCtx | indent 8 }}
 {{ include "hpcc.addDllVolumeMount" $commonCtx | indent 8 }}
 {{ include "hpcc.addSecretVolumeMounts" $commonCtx | indent 8 }}
+{{ include "hpcc.addCertificateVolumeMount" (dict "root" $ "component" $application "name" .name  "certificate" .certificate "external" (and (hasKey . "public") .public)) | indent 8 }}
       volumes:
 {{ include "hpcc.addConfigMapVolume" . | indent 6 }}
 {{ include "hpcc.addDataVolume" $commonCtx | indent 6 }}
 {{ include "hpcc.addDllVolume" $commonCtx | indent 6 }}
 {{ include "hpcc.addSecretVolumes" $commonCtx | indent 6 }}
+{{ include "hpcc.addCertificateVolume" (dict "root" $ "component" $application "name" .name "certificate" .certificate "external" (and (hasKey . "public") .public)) | indent 6 }}
 ---
 kind: ConfigMap 
 {{ include "hpcc.espConfigMap" $commonCtx }}
@@ -89,5 +113,6 @@ spec:
     run: {{ .name | quote }}
   type: {{ .public | ternary "LoadBalancer" "ClusterIP" }}
 ---
+{{ include "hpcc.addCertificate" (dict "root" $ "name" .name "service" . "component" $application "external" (and (hasKey . "public") .public)) }}
 {{- end }}
 {{- end }}

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

@@ -0,0 +1,31 @@
+{{- define "hpcc.issuers" -}}
+apiVersion: cert-manager.io/v1
+kind: {{ .me.kind | default "Issuer" }}
+metadata:
+  name: {{ .me.name }}
+  namespace: {{ .root.Release.Namespace | default "default" }}
+   {{- if .me.spec }}
+spec:
+  {{- if .me.spec.ca }}
+   {{- if .me.spec.ca.secretName }}
+    {{- if not (lookup "v1" "Secret" .root.Release.Namespace .me.spec.ca.secretName) }}
+      {{- $_ := fail (printf "Using a local certificate authority requires a CA certificate stored in the secret named '%s'. You can disable mTLS security by setting certificates.enabled=false" .me.spec.ca.secretName ) -}}
+    {{- end }}
+   {{- end }}
+  {{- end }}
+{{ toYaml .me.spec | indent 2 }}
+
+ {{- end }}
+{{- end }}
+
+{{- if $.Values.certificates -}}
+ {{- if $.Values.certificates.enabled -}}
+   {{- if not (.Capabilities.APIVersions.Has "cert-manager.io/v1") }}
+    {{- $_ := fail (printf "Enabling certificate generation requires cert-manager resources. Please intall cert-manager. You can disable mTLS security by setting certificates.enabled=false" ) -}}
+   {{- end -}}
+  {{- range $k, $v := .Values.certificates.issuers }}
+{{- include "hpcc.issuers" (dict "root" $ "me" $v ) }}
+---
+  {{- end }}
+ {{- end }}
+{{- end }}

+ 11 - 1
helm/hpcc/templates/localroxie.yaml

@@ -80,11 +80,17 @@ spec:
 {{ include "hpcc.addDataVolumeMount" $commonCtx | indent 8 }}
 {{ include "hpcc.addDllVolumeMount" $commonCtx | indent 8 }}
 {{- include "hpcc.addSecretVolumeMounts" $commonCtx | indent 8 }}
+{{ include "hpcc.addCertificateVolumeMount" (dict "root" $ "name" $roxie.name "component" "localroxie" "external" false) | indent 8 }}
+{{ include "hpcc.addCertificateVolumeMount" (dict "root" $ "name" $roxie.name "component" "localroxie" "external" true) | indent 8 }}
+{{ include "hpcc.addUDPCertificateVolumeMount" (dict "root" $ "name" $roxie.name "component" "localudpkey" ) | indent 8 }}
       volumes:
 {{ include "hpcc.addConfigMapVolume" . | indent 6 }}
 {{ include "hpcc.addDataVolume" $commonCtx | indent 6 }}
 {{ include "hpcc.addDllVolume" $commonCtx | indent 6 }}
 {{ include "hpcc.addSecretVolumes" $commonCtx | indent 6 }}
+{{ include "hpcc.addCertificateVolume" (dict "root" $ "name" $roxie.name "component" "localroxie" "external" false) | indent 6 }}
+{{ include "hpcc.addCertificateVolume" (dict "root" $ "name" $roxie.name "component" "localroxie" "external" true) | indent 6 }}
+{{ include "hpcc.addUDPCertificateVolume" (dict "root" $ "name" $roxie.name "component" "localudpkey" ) | indent 6 }}
 ---
 {{- range $service := $roxie.services }}
 {{- if ne (int $service.port)  0 }}
@@ -102,12 +108,16 @@ spec:
   selector:
     roxie-server: {{ $servername | quote }}
   type: {{ if $service.external -}} LoadBalancer {{- else -}} ClusterIP {{- end }}
+---
 {{- end }}
 {{- end }}
----
 kind: ConfigMap 
 {{ include "hpcc.localroxieConfigMap" $commonCtx }}
 ---
+{{ include "hpcc.addCertificate" (dict "root" $ "name" $roxie.name "services" $roxie.services "component" "localroxie" "external" false) }}
+{{ include "hpcc.addCertificate" (dict "root" $ "name" $roxie.name "services" $roxie.services "component" "localroxie" "external" true) }}
+{{ include "hpcc.addUDPCertificate" (dict "root" $ "name" $roxie.name "component" "localudpkey") }}
+
 {{- end }}
 {{- end }}
 {{- end }}

+ 33 - 4
helm/hpcc/templates/roxie.yaml

@@ -48,6 +48,7 @@ data:
 {{- if not $roxie.localAgent -}}
 {{- $_ := set $roxie "localAgent" false -}}
 {{- $servername := printf "%s-server" $roxie.name -}}
+{{- $udpkeyname := $roxie.name -}}
 
 apiVersion: apps/v1
 kind: Deployment
@@ -83,10 +84,16 @@ spec:
               ]
         volumeMounts:
 {{ include "hpcc.addConfigMapVolumeMount" $toposerver | indent 8 }}
+{{ include "hpcc.addCertificateVolumeMount" (dict "root" $ "component" "topo" "name" $commonCtx.toponame "external" false) | indent 8 }}
       volumes:
 {{ include "hpcc.addConfigMapVolume" $toposerver | indent 6 }}
+{{ include "hpcc.addCertificateVolume" (dict "root" $ "component" "topo" "name" $commonCtx.toponame "external" false) | indent 6 }}
+
+{{ include "hpcc.addCertificate" (dict "root" $ "name" $commonCtx.toponame "servicename" $commonCtx.toponame "component" "topo" "external" false) }}
+{{ include "hpcc.addUDPCertificate" (dict "root" $ "name" $udpkeyname "component" "udpkey") }}
 ---
 
+---
 {{- range $service := $roxie.services }}
 {{- if ne (int $service.port)  0 }}
 apiVersion: v1
@@ -103,11 +110,10 @@ spec:
   selector:
     roxie-server: {{ $servername | quote }}
   type: {{ if $service.external -}} LoadBalancer {{- else -}} ClusterIP {{- end }}
+---
 {{- end }}
 {{- end }}
 
----
-
 apiVersion: v1
 kind: Service
 metadata:
@@ -120,7 +126,6 @@ spec:
   selector:
     run: {{ $commonCtx.toponame | quote }}
   clusterIP: None # Headless service
-
 ---
 
 apiVersion: networking.k8s.io/v1
@@ -207,18 +212,31 @@ spec:
 {{ include "hpcc.addDataVolumeMount" $commonCtx | indent 8 }}
 {{ include "hpcc.addDllVolumeMount" $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.addUDPCertificateVolumeMount" (dict "root" $ "component" "udpkey" "name" $udpkeyname ) | indent 8 }}
       volumes:
 {{ include "hpcc.addConfigMapVolume" $roxie | indent 6 }}
 {{ include "hpcc.addDataVolume" $commonCtx | indent 6 }}
 {{ include "hpcc.addDllVolume" $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.addUDPCertificateVolume" (dict "root" $ "component" "udpkey" "name" $udpkeyname) | indent 6 }}
+
+---
+{{ include "hpcc.addCertificate" (dict "root" $ "name" $servername "services" $roxie.services "component" "roxie-server" "external" false) }}
+{{ include "hpcc.addCertificate" (dict "root" $ "name" $servername "services" $roxie.services "component" "roxie-server" "external" true) }}
 ---
 
 {{ end -}}
 {{ range $c, $e := until ($commonCtx.numChannels|int) -}}
 {{- $channel := add $c 1 -}}
-{{- $name := printf "%s-agent-%d" $roxie.name $channel -}}
+{{- $name := printf "%s-agent-%d" $roxie.name $channel }}
 
+{{ include "hpcc.addCertificate" (dict "root" $ "name" $name "services" $roxie.services "component" "roxie-agent" "external" false) }}
+{{ include "hpcc.addCertificate" (dict "root" $ "name" $name "services" $roxie.services "component" "roxie-agent" "external" true) }}
+---
 apiVersion: apps/v1
 kind: Deployment
 metadata:
@@ -277,11 +295,22 @@ spec:
 {{ include "hpcc.addDataVolumeMount" $commonCtx | indent 8 }}
 {{ include "hpcc.addDllVolumeMount" $commonCtx | indent 8 }}
 {{ 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.addUDPCertificateVolumeMount" (dict "root" $ "component" "udpkey" "name" $udpkeyname ) | indent 8 }}
+{{- end }}
+
       volumes:
 {{ include "hpcc.addConfigMapVolume" $roxie | indent 6 }}
 {{ include "hpcc.addDataVolume" $commonCtx | indent 6 }}
 {{ include "hpcc.addDllVolume" $commonCtx | indent 6 }}
 {{ 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.addUDPCertificateVolume" (dict "root" $ "component" "udpkey" "name" $udpkeyname) | indent 6 }}
+{{- end }}
 
 ---
 

+ 24 - 0
helm/hpcc/templates/thor.yaml

@@ -78,6 +78,7 @@ data:
 {{ include "hpcc.addDataVolumeMount" . | indent 12 }}
 {{ include "hpcc.addDllVolumeMount" . | indent 12 }}
 {{ include "hpcc.addSecretVolumeMounts" . | indent 12 }}
+{{ include "hpcc.addCertificateVolumeMount" (dict "root" .root "name" .me.name "component" "eclagent") | indent 12 }}
 {{- if $misc.postJobCommandViaSidecar }}
 {{ include "hpcc.addWaitAndRunVolumeMount" . | indent 12 }}
 {{- end }}
@@ -86,6 +87,7 @@ data:
 {{ include "hpcc.addDataVolume" . | indent 10 }}
 {{ include "hpcc.addDllVolume" . | indent 10 }}
 {{ include "hpcc.addSecretVolumes" . | indent 10 }}
+{{ include "hpcc.addCertificateVolume" (dict "root" .root "name" .me.name "component" "eclagent") | indent 10 }}
 {{- if $misc.postJobCommandViaSidecar }}
 {{ include "hpcc.addWaitAndRunVolume" . | indent 10 }}
 {{- end }}
@@ -131,6 +133,7 @@ data:
 {{ include "hpcc.addDataVolumeMount" . | indent 12 }}
 {{ include "hpcc.addDllVolumeMount" . | indent 12 }}
 {{ include "hpcc.addSecretVolumeMounts" . | indent 12 }}
+{{ include "hpcc.addCertificateVolumeMount" (dict "root" .root "name" .me.name "component" "thormanager") | indent 12 }}
 {{- if $misc.postJobCommandViaSidecar }}
 {{ include "hpcc.addWaitAndRunVolumeMount" . | indent 12 }}
 {{- end }}
@@ -139,6 +142,7 @@ data:
 {{ include "hpcc.addDataVolume" . | indent 10 }}
 {{ include "hpcc.addDllVolume" . | indent 10 }}
 {{ include "hpcc.addSecretVolumes" . | indent 10 }}
+{{ include "hpcc.addCertificateVolume" (dict "root" .root "name" .me.name "component" "thormanager") | indent 10 }}
 {{- if $misc.postJobCommandViaSidecar }}
 {{ include "hpcc.addWaitAndRunVolume" . | indent 10 }}
 {{- end }}
@@ -181,6 +185,7 @@ data:
 {{ include "hpcc.addDataVolumeMount" . | indent 12 }}
 {{ include "hpcc.addDllVolumeMount" . | indent 12 }}
 {{ include "hpcc.addSecretVolumeMounts" . | indent 12 }}
+{{ include "hpcc.addCertificateVolumeMount" (dict "root" .root "name" .me.name "component" "thorworker") | indent 12 }}
 {{- if $misc.postJobCommandViaSidecar }}
 {{ include "hpcc.addWaitAndRunVolumeMount" . | indent 12 }}
 {{- end }}
@@ -189,6 +194,7 @@ data:
 {{ include "hpcc.addDataVolume" . | indent 10 }}
 {{ include "hpcc.addDllVolume" . | indent 10 }}
 {{ include "hpcc.addSecretVolumes" . | indent 10 }}
+{{ include "hpcc.addCertificateVolume" (dict "root" .root "name" .me.name "component" "thorworker") | indent 10 }}
 {{- if $misc.postJobCommandViaSidecar }}
 {{ include "hpcc.addWaitAndRunVolume" . | indent 10 }}
 {{- end }}
@@ -219,6 +225,7 @@ data:
               job: %jobname
 {{- end -}}
 
+{{- $local := dict "first" true }}
 {{ range $.Values.thor -}}
 {{- if not .disabled -}}
 {{- $secretsCategories := list "system" "ecl-user" "ecl" "storage" }}
@@ -271,11 +278,13 @@ spec:
 {{ include "hpcc.addDataVolumeMount" $commonCtx | indent 8 }}
 {{ include "hpcc.addDllVolumeMount" $commonCtx | indent 8 }}
 {{ include "hpcc.addSecretVolumeMounts" $commonCtx | indent 8 }}
+{{ include "hpcc.addCertificateVolumeMount" (dict "root" $ "name" $commonCtx.eclAgentName "component" "eclagent") | indent 8 }}
       volumes:
 {{ include "hpcc.addConfigMapVolume" . | indent 6 }}
 {{ include "hpcc.addDataVolume" $commonCtx | indent 6 }}
 {{ include "hpcc.addDllVolume" $commonCtx | indent 6 }}
 {{ include "hpcc.addSecretVolumes" $commonCtx | indent 6 }}
+{{ include "hpcc.addCertificateVolume" (dict "root" $ "name" $commonCtx.eclAgentName "component" "eclagent") | indent 6 }}
 ---
 apiVersion: apps/v1
 kind: Deployment
@@ -317,14 +326,29 @@ spec:
 {{ include "hpcc.addDataVolumeMount" $commonCtx | indent 8 }}
 {{ include "hpcc.addDllVolumeMount" $commonCtx | indent 8 }}
 {{ include "hpcc.addSecretVolumeMounts" $commonCtx | indent 8 }}
+{{ include "hpcc.addCertificateVolumeMount" (dict "root" $ "name" $commonCtx.thorAgentName "component" "thoragent") | indent 8 }}
       volumes:
 {{ include "hpcc.addConfigMapVolume" . | indent 6 }}
 {{ include "hpcc.addDataVolume" $commonCtx | indent 6 }}
 {{ include "hpcc.addDllVolume" $commonCtx | indent 6 }}
 {{ include "hpcc.addSecretVolumes" $commonCtx | indent 6 }}
+{{ include "hpcc.addCertificateVolume" (dict "root" $ "name" $commonCtx.thorAgentName "component" "thoragent") | indent 6 }}
 ---
 kind: ConfigMap
 {{ include "hpcc.thorConfigMap" $commonCtx }}
 ---
+{{ include "hpcc.addCertificate" (dict "root" $ "name" $commonCtx.eclAgentName "component" "eclagent") }}
+{{ include "hpcc.addCertificate" (dict "root" $ "name" $commonCtx.thorAgentName "component" "thoragent") }}
+{{- if $local.first }}
+{{- $_ := set $local "first" false }}
+## thorworker and thormanager pods have unique names by workunit not by cluster. So we have to use a global certificate.
+## create these only once
+{{ include "hpcc.addCertificate" (dict "root" $ "name" "thormanager-w" "component" "thormanager") }}
+{{ include "hpcc.addCertificate" (dict "root" $ "name" "thorworker-w" "component" "thorworker") }}
+{{- end }}
+
+{{- if $commonCtx.eclAgentUseChildProcesses }}
+{{ include "hpcc.addCertificate" (dict "root" $ "name" .name "component" "eclagent") }}
+{{- end }}
 {{- end }}
 {{- end }}

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

@@ -54,6 +54,26 @@
       },
       "additionalProperties": false
     },
+    "certificates": {
+      "type": "object",
+      "additionalProperties": false,
+      "properties": {
+        "enabled": {
+          "type": "boolean"
+        },
+        "issuers": {
+          "type": "object",
+          "properties": {
+            "local": {
+              "$ref": "#/definitions/issuer"
+            },
+            "public": {
+              "$ref": "#/definitions/issuer"
+            }
+          }
+        }
+      }
+    },
     "secrets": {
       "description": "configuration for secrets accessed by the components",
       "type": "object",
@@ -1101,6 +1121,27 @@
           "type": "integer"
         }
       }
+    },
+    "issuer": {
+      "type": "object",
+      "required": [ "name" ],
+      "properties": {
+        "name": {
+          "type": "string",
+          "description": "The name of the issuer which will be referenced in certificate objects"
+        },
+        "kind": {
+          "type": "string",
+          "enum": [
+              "Issuer",
+              "ClusterIssuer"
+          ]
+        },
+        "spec": {
+          "description": "The cert-manager spec for the issuer. Should match issuer spec(s) defined by https://cert-manager.io/docs/configuration/",
+          "type": "object"
+        }
+      }
     }
   }
 }

+ 48 - 9
helm/hpcc/values.yaml

@@ -115,6 +115,53 @@ storage:
     # existingClaim: ""
     # forcePermissions: false
 
+## The certificates section can be used to enable cert-manager to generate TLS certificates for each component in the hpcc.
+## You must first install cert-manager to use this feature.
+## https://cert-manager.io/docs/installation/kubernetes/#installing-with-helm
+##
+## The Certificate issuers are divided into "local" (those which will be used for local mutual TLS) and "public" those
+## which will be publicly accesible and therefore need to be recognized by browsers and/or other entities.
+##
+## Both public and local issuers have a spec section. The contents of the "spec" are documented in the cer-manager
+## "Issuer configuration" documentation. https://cert-manager.io/docs/configuration/#supported-issuer-types
+##
+## The default configuration is meant to provide reasonable functionality without additional dependencies.
+##
+## Public issuers can be tricky if you want browsers to recognize the certificates.  This is a complex topic outside the scope
+## of this comment.  The default for the public issuer generates self signed certifiecates. The expectation is that this will be
+## overriden by the configuration of an external certificate authority or vault in QA and production environments.
+##
+## The default for the local (mTLS) issuer is designed to act as our own local certificate authority. We need only need to recognize
+## what a component is, and that it belongs to this cluster.
+## But a kubernetes secret must be provided for the certificate authority key-pair.  The default name for the secret
+## is "local-issuer-ca-key-pair". The secret is a standard kubernetes.io/tls secret and should provide data values for
+## "tls.crt" and "tls.key".
+##
+## The local issuer can also be configured to use an external certificate authority or vault.
+##
+certificates:
+  enabled: false
+  issuers:
+    local:
+      name: hpcc-local-issuer
+      ## kind can be changed to ClusterIssue to refer to a ClusterIssuer. https://cert-manager.io/docs/reference/api-docs/#cert-manager.io/v1.ClusterIssuer
+      kind: Issuer
+      ## do not define spec (set spec: null), to reference an Issuer resource that already exists in the cluster
+      ## change spec if you'd like to change how certificates get issued... see ## https://cert-manager.io/docs/configuration/#supported-issuer-types
+      ## for information on what spec should contain.
+      spec:
+        ca:
+          secretName: hpcc-local-issuer-key-pair
+    public:
+      name: hpcc-public-issuer
+      ## kind can be changed to ClusterIssue to refer to a ClusterIssuer. https://cert-manager.io/docs/reference/api-docs/#cert-manager.io/v1.ClusterIssuer
+      kind: Issuer
+      ## do not define spec (set spec: null), to reference an Issuer resource that already exists in the cluster
+      ## change spec if you'd like to change how certificates get issued... see ## https://cert-manager.io/docs/configuration/#supported-issuer-types
+      ## for information on what spec should contain.
+      spec:
+        selfSigned: {}
+
 ## The secrets section contains a set of categories, each of which contain a list of secrets.  The categories deterime which
 ## components have access to the secrets.
 ## For each secret:
@@ -302,7 +349,6 @@ esp:
   ## Pre-configured esp applications include eclwatch, eclservices, and eclqueries
   application: eclwatch
   auth: none
-  tls: off
   replicas: 1
   ## port can be used to change the local port used by the pod. If omitted, the default port (8880) is used
   port: 8888
@@ -313,47 +359,40 @@ esp:
   #resources:
   #  cpu: "1"
   #  memory: "2G"
-
 - name: eclservices
   application: eclservices
   auth: none
-  tls: off
   replicas: 1
   servicePort: 8010
   public: false
   #resources:
   #  cpu: "250m"
   #  memory: "1G"
-
 - name: eclqueries
   application: eclqueries
   auth: none
-  tls: off
   replicas: 1
   public: true
   servicePort: 8002
   #resources:
   #  cpu: "250m"
   #  memory: "1G"
-
 - name: esdl-sandbox
   application: esdl-sandbox
   auth: none
-  tls: off
   replicas: 1
   public: true
   servicePort: 8899
   #resources:
   #  cpu: "250m"
   #  memory: "1G"
-
 - name: sql2ecl
   application: sql2ecl
   auth: none
-  tls: off
   replicas: 1
   public: true
   servicePort: 8510
+  #domain: hpccsql.com
   #resources:
   #  cpu: "250m"
   #  memory: "1G"

+ 23 - 1
plugins/fileservices/fileservices.cpp

@@ -225,6 +225,14 @@ static void addConfiguredWsFSUrl(const char * url)
     availableWsFS.appendUniq(url);
 }
 
+static void setContainerLocalCertificate(IEspClientRpcSettings &rpc)
+{
+#ifdef _CONTAINERIZED
+    //will only affect HTTPS
+    rpc.setMtlsSecretName("local");
+#endif
+}
+
 static bool contactWsFS(const char * espurl, IConstWorkUnit * wu)
 {
     CClientFileSpray server;
@@ -234,6 +242,7 @@ static bool contactWsFS(const char * espurl, IConstWorkUnit * wu)
     try
     {
         Owned<IClientEchoDateTime> req = server.createEchoDateTimeRequest();
+        setContainerLocalCertificate(req->rpc());
         Owned<IClientEchoDateTimeResponse> result = server.EchoDateTime(req);
 
         if (result->getResult())
@@ -658,6 +667,7 @@ static void blockUntilComplete(const char * label, IClientFileSpray &server, ICo
     {
 
         Owned<IClientGetDFUWorkunit> req = server.createGetDFUWorkunitRequest();
+        setContainerLocalCertificate(req->rpc());
         req->setWuid(wuid);
         Owned<IClientGetDFUWorkunitResponse> result = server.GetDFUWorkunit(req);
 
@@ -721,6 +731,7 @@ static void blockUntilComplete(const char * label, IClientFileSpray &server, ICo
         if (aborting)
         {
             Owned<IClientAbortDFUWorkunit> abortReq = server.createAbortDFUWorkunitRequest();
+            setContainerLocalCertificate(abortReq->rpc());
             abortReq->setWuid(wuid);
             Linked<IClientAbortDFUWorkunitResponse> abortResp = server.AbortDFUWorkunit(abortReq);
 
@@ -765,6 +776,7 @@ FILESERVICES_API char * FILESERVICES_CALL implementSprayFixed(ICodeContext *ctx,
     setServerAccess(server, wu);
 
     Owned<IClientSprayFixed> req = server.createSprayFixedRequest();
+    setContainerLocalCertificate(req->rpc());
     StringBuffer logicalName;
     constructLogicalName(wu, destinationLogicalName, logicalName);
 
@@ -866,6 +878,7 @@ static char * implementSprayVariable(ICodeContext *ctx, const char * sourceIP, c
     setServerAccess(server, wu);
 
     Owned<IClientSprayVariable> req = server.createSprayVariableRequest();
+    setContainerLocalCertificate(req->rpc());
     StringBuffer logicalName;
     constructLogicalName(wu, destinationLogicalName, logicalName);
 
@@ -1017,6 +1030,7 @@ FILESERVICES_API char * FILESERVICES_CALL implementSprayXml(ICodeContext *ctx, c
     setServerAccess(server, wu);
 
     Owned<IClientSprayVariable> req = server.createSprayVariableRequest();
+    setContainerLocalCertificate(req->rpc());
     StringBuffer logicalName;
     constructLogicalName(wu, destinationLogicalName, logicalName);
 
@@ -1126,6 +1140,7 @@ FILESERVICES_API char * FILESERVICES_CALL implementSprayJson(ICodeContext *ctx,
     setServerAccess(server, wu);
 
     Owned<IClientSprayVariable> req = server.createSprayVariableRequest();
+    setContainerLocalCertificate(req->rpc());
     StringBuffer logicalName;
     constructLogicalName(wu, destinationLogicalName, logicalName);
 
@@ -1220,6 +1235,7 @@ FILESERVICES_API char * FILESERVICES_CALL fsfDespray(ICodeContext *ctx, const ch
         setServerAccess(server, wu);
 
     Owned<IClientDespray> req = server.createDesprayRequest();
+    setContainerLocalCertificate(req->rpc());
     StringBuffer logicalName;
     constructLogicalName(wu, sourceLogicalName, logicalName);
 
@@ -1265,6 +1281,7 @@ FILESERVICES_API char * FILESERVICES_CALL implementCopy(ICodeContext *ctx, const
     setServerAccess(server, wu);
 
     Owned<IClientCopy> req = server.createCopyRequest();
+    setContainerLocalCertificate(req->rpc());
     if (asSuperfile)
         req->setSuperCopy(true);
 
@@ -1363,6 +1380,7 @@ FILESERVICES_API char * FILESERVICES_CALL fsfReplicate(ICodeContext *ctx, const
     setServerAccess(server, wu);
 
     Owned<IClientReplicate> req = server.createReplicateRequest();
+    setContainerLocalCertificate(req->rpc());
     StringBuffer logicalName;
     constructLogicalName(wu, sourceLogicalName, logicalName);
 
@@ -1894,6 +1912,7 @@ FILESERVICES_API void FILESERVICES_CALL fslAbortDfuWorkunit(ICodeContext *ctx, c
     server.addServiceUrl(getAccessibleEspServerURL(espServerIpPort,wu));
     setServerAccess(server, wu);
     Owned<IClientAbortDFUWorkunit> abortReq = server.createAbortDFUWorkunitRequest();
+    setContainerLocalCertificate(abortReq->rpc());
     abortReq->setWuid(wuid);
     Linked<IClientAbortDFUWorkunitResponse> abortResp = server.AbortDFUWorkunit(abortReq);
     StringBuffer s("DFU Workunit Abort Requested for ");
@@ -1917,6 +1936,7 @@ FILESERVICES_API char *  FILESERVICES_CALL fsfMonitorLogicalFileName(ICodeContex
     if (shotcount == 0)
         shotcount = -1;
     Owned<IClientDfuMonitorRequest> req = server.createDfuMonitorRequest();
+    setContainerLocalCertificate(req->rpc());
     req->setEventName(eventname);
     req->setLogicalName(lfn);
     req->setShotLimit(shotcount);
@@ -1946,6 +1966,7 @@ FILESERVICES_API char *  FILESERVICES_CALL fsfMonitorFile(ICodeContext *ctx, con
         shotcount = -1;
 
     Owned<IClientDfuMonitorRequest> req = server.createDfuMonitorRequest();
+    setContainerLocalCertificate(req->rpc());
     req->setEventName(eventname);
     req->setIp(ip);
     req->setFilename(filename);
@@ -2233,6 +2254,7 @@ FILESERVICES_API char * FILESERVICES_CALL fsfRemotePull_impl(ICodeContext *ctx,
     setServerAccess(server, wu);
 
     Owned<IClientCopy> req = server.createCopyRequest();
+    setContainerLocalCertificate(req->rpc());
     if (asSuperfile)
         req->setSuperCopy(true);
 
@@ -2916,7 +2938,7 @@ FILESERVICES_API char * FILESERVICES_CALL fsGetEspURL(const char *username, cons
         }
         // MORE - if not found, we could generate a warning - it implies something misconfigured!
 
-        VStringBuffer espURL("%s://%s%s:%u", protocol, credentials.str(), defaultEsp, port);
+        VStringBuffer espURL("mtls:%s://%s%s:%u", protocol, credentials.str(), defaultEsp, port);
         return espURL.detach();
     }
 #else

+ 1 - 0
roxie/ccd/ccdmain.cpp

@@ -28,6 +28,7 @@
 #include <jfile.hpp>
 #include <jencrypt.hpp>
 #include "jutil.hpp"
+#include "jsecrets.hpp"
 #include <build-config.h>
 #include <udptopo.hpp>
 

+ 75 - 0
system/jlib/jsecrets.cpp

@@ -44,6 +44,10 @@
 #pragma GCC diagnostic pop
 #endif
 
+#ifdef _USE_OPENSSL
+#include <openssl/x509v3.h>
+#endif
+
 #include <vector>
 
 enum class CVaultKind { kv_v1, kv_v2 };
@@ -66,11 +70,14 @@ interface IVaultManager : extends IInterface
 
 static CriticalSection secretCacheCS;
 static Owned<IPropertyTree> secretCache;
+static CriticalSection mtlsInfoCacheCS;
+static Owned<IPropertyTree> mtlsInfoCache;
 static Owned<IVaultManager> vaultManager;
 
 MODULE_INIT(INIT_PRIORITY_SYSTEM)
 {
     secretCache.setown(createPTree());
+    mtlsInfoCache.setown(createPTree());
     return true;
 }
 
@@ -78,6 +85,7 @@ MODULE_EXIT()
 {
     vaultManager.clear();
     secretCache.clear();
+    mtlsInfoCache.clear();
 }
 
 static void splitUrlAddress(const char *address, size_t len, StringBuffer &host, StringBuffer *port)
@@ -711,3 +719,70 @@ extern jlib_decl bool getSecretValue(StringBuffer & result, const char *category
     return true;
 }
 
+bool getSecretUdpKey(MemoryAttr &updkey)
+{
+    bool ret = false;
+    updkey.clear();
+#if defined(_CONTAINERIZED) && defined(_USE_OPENSSL)
+    BIO *in = BIO_new_file("/opt/HPCCSystems/secrets/certificates/udp/tls.key", "r");
+    if (in == nullptr)
+        return false;
+    EC_KEY *eckey = PEM_read_bio_ECPrivateKey(in, nullptr, nullptr, nullptr);
+    if (eckey)
+    {
+        unsigned char *priv = NULL;
+        size_t privlen = EC_KEY_priv2buf(eckey, &priv);
+        if (privlen != 0)
+        {
+            updkey.set(privlen, priv);
+            OPENSSL_clear_free(priv, privlen);
+            ret = true;
+        }
+        EC_KEY_free(eckey);
+    }
+    BIO_free(in);
+#endif
+    return ret;
+}
+
+IPropertyTree *queryMtlsSecretInfo(const char *name)
+{
+    if (isEmptyString(name))
+        return nullptr;
+   CriticalBlock block(mtlsInfoCacheCS);
+   IPropertyTree *info = mtlsInfoCache->queryPropTree(name);
+    if (info)
+        return info;
+
+    StringBuffer filepath;
+    StringBuffer secretpath;
+
+    addPathSepChar(secretpath.append(ensureSecretDirectory())).append("certificates").append(PATHSEPCHAR).append(name).append(PATHSEPCHAR);
+
+    filepath.set(secretpath).append("tls.crt");
+    if (!checkFileExists(filepath))
+        return nullptr;
+
+    info = mtlsInfoCache->setPropTree(name);
+    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 (verify)
+    {
+        filepath.set(secretpath).append("ca.crt");
+        if (checkFileExists(filepath))
+        {
+            IPropertyTree *ca = ensurePTree(verify, "ca_certificates");
+            if (ca)
+                ca->setProp("@path", filepath.str());
+        }
+        verify->setPropBool("@enable", true);
+        verify->setPropBool("@address_match", false);
+        verify->setPropBool("@accept_selfsigned", false);
+        verify->setProp("trusted_peers", "anyone");
+
+    }
+    return info;
+}

+ 2 - 0
system/jlib/jsecrets.hpp

@@ -32,6 +32,8 @@ extern jlib_decl IPropertyTree *getSecret(const char *category, const char * nam
 extern jlib_decl bool getSecretKeyValue(MemoryBuffer & result, IPropertyTree *secret, const char * key);
 extern jlib_decl bool getSecretKeyValue(StringBuffer & result, IPropertyTree *secret, const char * key);
 extern jlib_decl bool getSecretValue(StringBuffer & result, const char *category, const char * name, const char * key, bool required);
+extern jlib_decl bool getSecretUdpKey(MemoryAttr &updkey);
+extern jlib_decl IPropertyTree *queryMtlsSecretInfo(const char *name);
 
 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);

+ 12 - 0
system/security/securesocket/securesocket.cpp

@@ -23,6 +23,7 @@
 //jlib
 #include "jliball.hpp"
 #include "string.h"
+#include "jsecrets.hpp"
 
 #ifdef _WIN32
 #include <windows.h>
@@ -1613,6 +1614,17 @@ SECURESOCKET_API ISecureSocketContext* createSecureSocketContextEx2(IPropertyTre
     }
 }       
 
+SECURESOCKET_API ISecureSocketContext* createSecureSocketContextSecret(const char *mtlsSecretName, SecureSocketType sockettype)
+{
+    IPropertyTree *info = queryMtlsSecretInfo(mtlsSecretName);
+    //if the secret doesn't exist doesn't exist just go on without it. IF it is required the tls connection will fail. 
+    //This is primarily for client side... server side would probably use the explict ptree config or explict cert param at least for now.
+    if (info)
+        return createSecureSocketContextEx2(info, sockettype);
+    else
+        return createSecureSocketContext(sockettype);
+}
+
 SECURESOCKET_API ICertificate *createCertificate()
 {
     return new securesocket::CRsaCertificate();

+ 2 - 0
system/security/securesocket/securesocket.hpp

@@ -80,12 +80,14 @@ interface ICertificate : implements IInterface
 typedef ISecureSocketContext* (*createSecureSocketContext_t)(SecureSocketType);
 typedef ISecureSocketContext* (*createSecureSocketContextEx_t)(const char* certfile, const char* privkeyfile, const char* passphrase, SecureSocketType);
 typedef ISecureSocketContext* (*createSecureSocketContextEx2_t)(IPropertyTree* config, SecureSocketType);
+typedef ISecureSocketContext* (*createSecureSocketContextSecret_t)(const char *mtlsSecretName, SecureSocketType);
 
 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* createSecureSocketContextSecret(const char *mtlsSecretName, SecureSocketType);
 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);
 };