This is an automated email from the ASF dual-hosted git repository.

thelabdude pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/solr-operator.git


The following commit(s) were added to refs/heads/main by this push:
     new eb56afc  Add config options to enable TLS from cert files mounted to a 
dir on each pod by a cert-manager extension (#292)
eb56afc is described below

commit eb56afc2a2df4f5ff856b5af7c21b5e31a8064bc
Author: Timothy Potter <[email protected]>
AuthorDate: Tue Aug 3 15:14:28 2021 -0600

    Add config options to enable TLS from cert files mounted to a dir on each 
pod by a cert-manager extension (#292)
    
    * Config options to enable TLS from cert files mounted to a dir on each pod 
by a cert-manager extension
    
    * update auto-generated copy
    
    * Remove some unrelated changes and clean-up docs
    
    * BusyBoxImage for Prometheus exporter
    
    * Make ingress test less flakey
    
    * MountedTLSDir unit test
    
    * Unit test for mountedTLSDir in prom exporter config
    
    * Refactor test for better reuse of initContainer related checks
    
    * Move util code out of API to util as it doesn't need to be exposed in the 
API
    
    * Up the default probe timeout for basicAuth
    
    * Clean-up secureProbe command TLS options and rename mountedTLSDir to 
mountedServerTLSDir to support a mountedClientTLSDir option in the future
    
    * Add MountedTLSDir test with basic auth enabled
    
    * Update comments
    
    * Add basic docs for mountedServerTLSDir option
    
    * Helm chart changelog entry
    
    Co-authored-by: Houston Putman <[email protected]>
---
 api/v1beta1/solrcloud_types.go                     |  37 +++-
 api/v1beta1/solrprometheusexporter_types.go        |  16 ++
 api/v1beta1/zz_generated.deepcopy.go               |  25 +++
 config/crd/bases/solr.apache.org_solrclouds.yaml   |  28 ++-
 .../solr.apache.org_solrprometheusexporters.yaml   |  41 +++-
 controllers/controller_utils_test.go               |  98 +++++++++-
 controllers/solrcloud_controller.go                |   4 +-
 controllers/solrcloud_controller_tls_test.go       | 130 ++++++++++++-
 controllers/solrprometheusexporter_controller.go   |   7 +-
 .../solrprometheusexporter_controller_test.go      |  95 +++++++++
 controllers/util/prometheus_exporter_util.go       |  55 +++++-
 controllers/util/solr_util.go                      | 213 ++++++++++++++++-----
 docs/solr-cloud/solr-cloud-crd.md                  |  15 +-
 docs/solr-prometheus-exporter/README.md            |   5 +
 helm/solr-operator/Chart.yaml                      |   7 +
 helm/solr-operator/crds/crds.yaml                  |  69 ++++++-
 16 files changed, 751 insertions(+), 94 deletions(-)

diff --git a/api/v1beta1/solrcloud_types.go b/api/v1beta1/solrcloud_types.go
index f08639e..b81d76c 100644
--- a/api/v1beta1/solrcloud_types.go
+++ b/api/v1beta1/solrcloud_types.go
@@ -1074,12 +1074,36 @@ const (
        Need ClientAuthType = "Need"
 )
 
+type MountedTLSDirectory struct {
+       // The path on the main Solr container where the TLS files are mounted 
by some external agent or CSI Driver
+       Path string `json:"path"`
+
+       // Override the name of the keystore file; defaults to keystore.p12
+       // +optional
+       KeystoreFile string `json:"keystoreFile,omitempty"`
+
+       // Override the name of the keystore password file; defaults to 
keystore-password
+       // +optional
+       KeystorePasswordFile string `json:"keystorePasswordFile,omitempty"`
+
+       // Override the name of the truststore file; defaults truststore.p12
+       // To use the same file as the keystore, override this variable with 
the name of your keystore file
+       // +optional
+       TruststoreFile string `json:"truststoreFile,omitempty"`
+
+       // Override the name of the truststore password file; defaults to the 
same value as the KeystorePasswordFile
+       // +optional
+       TruststorePasswordFile string `json:"truststorePasswordFile,omitempty"`
+}
+
 type SolrTLSOptions struct {
-       // TLS Secret containing a pkcs12 keystore
-       PKCS12Secret *corev1.SecretKeySelector `json:"pkcs12Secret"`
+       // TLS Secret containing a pkcs12 keystore; required unless 
mountedServerTLSDir is used
+       // +optional
+       PKCS12Secret *corev1.SecretKeySelector `json:"pkcs12Secret,omitempty"`
 
-       // Secret containing the key store password; this field is required as 
most JVMs do not support pkcs12 keystores without a password
-       KeyStorePasswordSecret *corev1.SecretKeySelector 
`json:"keyStorePasswordSecret"`
+       // Secret containing the key store password; this field is required 
unless mountedServerTLSDir is used, as most JVMs do not support pkcs12 
keystores without a password
+       // +optional
+       KeyStorePasswordSecret *corev1.SecretKeySelector 
`json:"keyStorePasswordSecret,omitempty"`
 
        // TLS Secret containing a pkcs12 truststore; if not provided, then the 
keystore and password are used for the truststore
        // The specified key is used as the truststore file name when mounted 
into Solr pods
@@ -1106,6 +1130,11 @@ type SolrTLSOptions struct {
        // Opt-in flag to restart Solr pods after TLS secret updates, such as 
if the cert is renewed; default is false.
        // +optional
        RestartOnTLSSecretUpdate bool 
`json:"restartOnTLSSecretUpdate,omitempty"`
+
+       // Used to specify a path where the keystore, truststore, and password 
files for the server certificate are mounted by an external agent or CSI driver.
+       // This option is typically used with 
`spec.updateStrategy.restartSchedule` to restart Solr pods before the mounted 
TLS cert expires.
+       // +optional
+       MountedServerTLSDir *MountedTLSDirectory 
`json:"mountedServerTLSDir,omitempty"`
 }
 
 // +kubebuilder:validation:Enum=Basic
diff --git a/api/v1beta1/solrprometheusexporter_types.go 
b/api/v1beta1/solrprometheusexporter_types.go
index 6decc90..3339354 100644
--- a/api/v1beta1/solrprometheusexporter_types.go
+++ b/api/v1beta1/solrprometheusexporter_types.go
@@ -56,6 +56,11 @@ type SolrPrometheusExporterSpec struct {
        // The xml config for the metrics
        // +optional
        Config string `json:"metricsConfig,omitempty"`
+
+       // An initContainer is needed to create a wrapper script around the 
exporter entrypoint when TLS is enabled
+       // with the `spec.solrReference.solrTLS.mountedServerTLSDir` option
+       // +optional
+       BusyBoxImage *ContainerImage `json:"busyBoxImage,omitempty"`
 }
 
 func (ps *SolrPrometheusExporterSpec) withDefaults(namespace string) (changed 
bool) {
@@ -237,6 +242,17 @@ func (sc *SolrPrometheusExporter) 
MetricsIngressUrl(ingressBaseUrl string) strin
        return fmt.Sprintf("%s.%s", sc.MetricsIngressPrefix(), ingressBaseUrl)
 }
 
+func (sc *SolrPrometheusExporter) BusyBoxImage() *ContainerImage {
+       c := sc.Spec.BusyBoxImage
+       if c == nil {
+               c = &ContainerImage{}
+               c.Repository = DefaultBusyBoxImageRepo
+               c.Tag = DefaultBusyBoxImageVersion
+               c.PullPolicy = DefaultPullPolicy
+       }
+       return c
+}
+
 //+kubebuilder:object:root=true
 
 // SolrPrometheusExporterList contains a list of SolrPrometheusExporter
diff --git a/api/v1beta1/zz_generated.deepcopy.go 
b/api/v1beta1/zz_generated.deepcopy.go
index f693e7d..0d7ee77 100644
--- a/api/v1beta1/zz_generated.deepcopy.go
+++ b/api/v1beta1/zz_generated.deepcopy.go
@@ -338,6 +338,21 @@ func (in *ManagedUpdateOptions) DeepCopy() 
*ManagedUpdateOptions {
 }
 
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, 
writing into out. in must be non-nil.
+func (in *MountedTLSDirectory) DeepCopyInto(out *MountedTLSDirectory) {
+       *out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, 
creating a new MountedTLSDirectory.
+func (in *MountedTLSDirectory) DeepCopy() *MountedTLSDirectory {
+       if in == nil {
+               return nil
+       }
+       out := new(MountedTLSDirectory)
+       in.DeepCopyInto(out)
+       return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, 
writing into out. in must be non-nil.
 func (in *PersistenceSource) DeepCopyInto(out *PersistenceSource) {
        *out = *in
        if in.S3 != nil {
@@ -1009,6 +1024,11 @@ func (in *SolrPrometheusExporterSpec) DeepCopyInto(out 
*SolrPrometheusExporterSp
                **out = **in
        }
        in.CustomKubeOptions.DeepCopyInto(&out.CustomKubeOptions)
+       if in.BusyBoxImage != nil {
+               in, out := &in.BusyBoxImage, &out.BusyBoxImage
+               *out = new(ContainerImage)
+               **out = **in
+       }
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, 
creating a new SolrPrometheusExporterSpec.
@@ -1104,6 +1124,11 @@ func (in *SolrTLSOptions) DeepCopyInto(out 
*SolrTLSOptions) {
                *out = new(v1.SecretKeySelector)
                (*in).DeepCopyInto(*out)
        }
+       if in.MountedServerTLSDir != nil {
+               in, out := &in.MountedServerTLSDir, &out.MountedServerTLSDir
+               *out = new(MountedTLSDirectory)
+               **out = **in
+       }
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, 
creating a new SolrTLSOptions.
diff --git a/config/crd/bases/solr.apache.org_solrclouds.yaml 
b/config/crd/bases/solr.apache.org_solrclouds.yaml
index e52277c..9f284bf 100644
--- a/config/crd/bases/solr.apache.org_solrclouds.yaml
+++ b/config/crd/bases/solr.apache.org_solrclouds.yaml
@@ -4576,7 +4576,7 @@ spec:
                     - Need
                     type: string
                   keyStorePasswordSecret:
-                    description: Secret containing the key store password; 
this field is required as most JVMs do not support pkcs12 keystores without a 
password
+                    description: Secret containing the key store password; 
this field is required unless mountedServerTLSDir is used, as most JVMs do not 
support pkcs12 keystores without a password
                     properties:
                       key:
                         description: The key of the secret to select from.  
Must be a valid secret key.
@@ -4590,8 +4590,29 @@ spec:
                     required:
                     - key
                     type: object
+                  mountedServerTLSDir:
+                    description: Used to specify a path where the keystore, 
truststore, and password files for the server certificate are mounted by an 
external agent or CSI driver. This option is typically used with 
`spec.updateStrategy.restartSchedule` to restart Solr pods before the mounted 
TLS cert expires.
+                    properties:
+                      keystoreFile:
+                        description: Override the name of the keystore file; 
defaults to keystore.p12
+                        type: string
+                      keystorePasswordFile:
+                        description: Override the name of the keystore 
password file; defaults to keystore-password
+                        type: string
+                      path:
+                        description: The path on the main Solr container where 
the TLS files are mounted by some external agent or CSI Driver
+                        type: string
+                      truststoreFile:
+                        description: Override the name of the truststore file; 
defaults truststore.p12 To use the same file as the keystore, override this 
variable with the name of your keystore file
+                        type: string
+                      truststorePasswordFile:
+                        description: Override the name of the truststore 
password file; defaults to the same value as the KeystorePasswordFile
+                        type: string
+                    required:
+                    - path
+                    type: object
                   pkcs12Secret:
-                    description: TLS Secret containing a pkcs12 keystore
+                    description: TLS Secret containing a pkcs12 keystore; 
required unless mountedServerTLSDir is used
                     properties:
                       key:
                         description: The key of the secret to select from.  
Must be a valid secret key.
@@ -4641,9 +4662,6 @@ spec:
                   verifyClientHostname:
                     description: Verify client's hostname during SSL handshake
                     type: boolean
-                required:
-                - keyStorePasswordSecret
-                - pkcs12Secret
                 type: object
               updateStrategy:
                 description: Define how Solr rolling updates are executed.
diff --git a/config/crd/bases/solr.apache.org_solrprometheusexporters.yaml 
b/config/crd/bases/solr.apache.org_solrprometheusexporters.yaml
index c54f2e7..5682b7f 100644
--- a/config/crd/bases/solr.apache.org_solrprometheusexporters.yaml
+++ b/config/crd/bases/solr.apache.org_solrprometheusexporters.yaml
@@ -60,6 +60,19 @@ spec:
           spec:
             description: SolrPrometheusExporterSpec defines the desired state 
of SolrPrometheusExporter
             properties:
+              busyBoxImage:
+                description: An initContainer is needed to create a wrapper 
script around the exporter entrypoint when TLS is enabled with the 
`spec.solrReference.solrTLS.mountedServerTLSDir` option
+                properties:
+                  imagePullSecret:
+                    type: string
+                  pullPolicy:
+                    description: PullPolicy describes a policy for if/when to 
pull a container image
+                    type: string
+                  repository:
+                    type: string
+                  tag:
+                    type: string
+                type: object
               customKubeOptions:
                 description: Provide custom options for kubernetes objects 
created for the SolrPrometheusExporter.
                 properties:
@@ -3445,7 +3458,7 @@ spec:
                         - Need
                         type: string
                       keyStorePasswordSecret:
-                        description: Secret containing the key store password; 
this field is required as most JVMs do not support pkcs12 keystores without a 
password
+                        description: Secret containing the key store password; 
this field is required unless mountedServerTLSDir is used, as most JVMs do not 
support pkcs12 keystores without a password
                         properties:
                           key:
                             description: The key of the secret to select from. 
 Must be a valid secret key.
@@ -3459,8 +3472,29 @@ spec:
                         required:
                         - key
                         type: object
+                      mountedServerTLSDir:
+                        description: Used to specify a path where the 
keystore, truststore, and password files for the server certificate are mounted 
by an external agent or CSI driver. This option is typically used with 
`spec.updateStrategy.restartSchedule` to restart Solr pods before the mounted 
TLS cert expires.
+                        properties:
+                          keystoreFile:
+                            description: Override the name of the keystore 
file; defaults to keystore.p12
+                            type: string
+                          keystorePasswordFile:
+                            description: Override the name of the keystore 
password file; defaults to keystore-password
+                            type: string
+                          path:
+                            description: The path on the main Solr container 
where the TLS files are mounted by some external agent or CSI Driver
+                            type: string
+                          truststoreFile:
+                            description: Override the name of the truststore 
file; defaults truststore.p12 To use the same file as the keystore, override 
this variable with the name of your keystore file
+                            type: string
+                          truststorePasswordFile:
+                            description: Override the name of the truststore 
password file; defaults to the same value as the KeystorePasswordFile
+                            type: string
+                        required:
+                        - path
+                        type: object
                       pkcs12Secret:
-                        description: TLS Secret containing a pkcs12 keystore
+                        description: TLS Secret containing a pkcs12 keystore; 
required unless mountedServerTLSDir is used
                         properties:
                           key:
                             description: The key of the secret to select from. 
 Must be a valid secret key.
@@ -3510,9 +3544,6 @@ spec:
                       verifyClientHostname:
                         description: Verify client's hostname during SSL 
handshake
                         type: boolean
-                    required:
-                    - keyStorePasswordSecret
-                    - pkcs12Secret
                     type: object
                   standalone:
                     description: Reference of a standalone solr instance
diff --git a/controllers/controller_utils_test.go 
b/controllers/controller_utils_test.go
index dace14f..c32a5a2 100644
--- a/controllers/controller_utils_test.go
+++ b/controllers/controller_utils_test.go
@@ -110,6 +110,7 @@ func expectIngress(g *gomega.GomegaWithT, requests chan 
reconcile.Request, expec
        // Delete the Ingress and expect Reconcile to be called for Ingress 
deletion
        g.Expect(testClient.Delete(context.TODO(), 
ingress)).NotTo(gomega.HaveOccurred())
        g.Eventually(requests, 
timeout).Should(gomega.Receive(gomega.Equal(expectedRequest)))
+       g.Eventually(requests, 
timeout).Should(gomega.Receive(gomega.Equal(expectedRequest)))
        g.Eventually(func() error { return testClient.Get(context.TODO(), 
ingressKey, ingress) }, timeout).
                Should(gomega.Succeed())
 
@@ -207,7 +208,7 @@ func createTLSOptions(tlsSecretName string, keystorePassKey 
string, restartOnTLS
                },
                PKCS12Secret: &corev1.SecretKeySelector{
                        LocalObjectReference: corev1.LocalObjectReference{Name: 
tlsSecretName},
-                       Key:                  util.Pkcs12KeystoreFile,
+                       Key:                  util.DefaultPkcs12KeystoreFile,
                },
                RestartOnTLSSecretUpdate: restartOnTLSSecretUpdate,
        }
@@ -236,6 +237,56 @@ func createBasicAuthSecret(name string, key string, ns 
string) *corev1.Secret {
        return &corev1.Secret{ObjectMeta: metav1.ObjectMeta{Name: name, 
Namespace: ns}, Data: secretData, Type: corev1.SecretTypeBasicAuth}
 }
 
+func expectInitdbVolumeMount(t *testing.T, podTemplate 
*corev1.PodTemplateSpec) {
+       assert.NotNil(t, podTemplate.Spec.Volumes)
+       var initdbVol *corev1.Volume = nil
+       for _, vol := range podTemplate.Spec.Volumes {
+               if vol.Name == "initdb" {
+                       initdbVol = &vol
+                       break
+               }
+       }
+       assert.NotNil(t, initdbVol, fmt.Sprintf("initdb volume not found in pod 
template; volumes: %v", podTemplate.Spec.Volumes))
+       assert.NotNil(t, initdbVol.VolumeSource.EmptyDir, "initdb volume should 
be an emptyDir")
+
+       assert.NotNil(t, podTemplate.Spec.Containers)
+       assert.True(t, len(podTemplate.Spec.Containers) > 0)
+       mainContainer := podTemplate.Spec.Containers[0]
+       var initdbMount *corev1.VolumeMount = nil
+       for _, m := range mainContainer.VolumeMounts {
+               if m.Name == "initdb" {
+                       initdbMount = &m
+                       break
+               }
+       }
+       assert.NotNil(t, initdbMount)
+       assert.Equal(t, util.InitdbPath, initdbMount.MountPath)
+}
+
+func expectInitContainer(t *testing.T, podTemplate *corev1.PodTemplateSpec, 
expName string, expVolMountName string, expVolMountPath string) 
*corev1.Container {
+       var expInitContainer *corev1.Container = nil
+       for _, cnt := range podTemplate.Spec.InitContainers {
+               if cnt.Name == expName {
+                       expInitContainer = &cnt
+                       break
+               }
+       }
+       assert.NotNil(t, expInitContainer, "Didn't find the "+expName+" 
InitContainer!")
+       assert.Equal(t, 3, len(expInitContainer.Command), "Wrong command length 
for "+expName+" init container")
+
+       var volMount *corev1.VolumeMount = nil
+       for _, m := range expInitContainer.VolumeMounts {
+               if m.Name == expVolMountName {
+                       volMount = &m
+                       break
+               }
+       }
+       assert.NotNil(t, volMount, "No "+expVolMountName+" volumeMount for 
"+expName+" InitContainer")
+       assert.Equal(t, expVolMountPath, volMount.MountPath, "Wrong mount path 
"+volMount.MountPath+" for "+expName+" InitContainer")
+
+       return expInitContainer
+}
+
 // Ensures all the TLS env vars, volume mounts and initContainers are setup 
for the PodTemplateSpec
 func expectTLSConfigOnPodTemplate(t *testing.T, tls *solr.SolrTLSOptions, 
podTemplate *corev1.PodTemplateSpec, needsPkcs12InitContainer bool) 
*corev1.Container {
        assert.NotNil(t, podTemplate.Spec.Volumes)
@@ -307,8 +358,8 @@ func expectTLSConfigOnPodTemplate(t *testing.T, tls 
*solr.SolrTLSOptions, podTem
 
        if tls.ClientAuth == solr.Need {
                // verify the probes use a command with SSL opts
-               tlsProps := "-Djavax.net.ssl.keyStore=$SOLR_SSL_KEY_STORE 
-Djavax.net.ssl.keyStorePassword=$SOLR_SSL_KEY_STORE_PASSWORD " +
-                       "-Djavax.net.ssl.trustStore=$SOLR_SSL_TRUST_STORE 
-Djavax.net.ssl.trustStorePassword=$SOLR_SSL_TRUST_STORE_PASSWORD"
+               tlsProps := "-Djavax.net.ssl.keyStore=$SOLR_SSL_KEY_STORE 
-Djavax.net.ssl.trustStore=$SOLR_SSL_TRUST_STORE" +
+                       " 
-Djavax.net.ssl.keyStorePassword=$SOLR_SSL_KEY_STORE_PASSWORD 
-Djavax.net.ssl.trustStorePassword=$SOLR_SSL_TRUST_STORE_PASSWORD"
                assert.NotNil(t, mainContainer.LivenessProbe, "main container 
should have a liveness probe defined")
                assert.NotNil(t, mainContainer.LivenessProbe.Exec, "liveness 
probe should have an exec when auth is enabled")
                assert.True(t, 
strings.Contains(mainContainer.LivenessProbe.Exec.Command[2], tlsProps), 
"liveness probe should invoke java with SSL opts")
@@ -320,6 +371,47 @@ func expectTLSConfigOnPodTemplate(t *testing.T, tls 
*solr.SolrTLSOptions, podTem
        return &mainContainer // return as a convenience in case tests want to 
do more checking on the main container
 }
 
+func expectMountedTLSDirEnvVars(t *testing.T, envVars []corev1.EnvVar) {
+       assert.NotNil(t, envVars)
+       envVars = filterVarsByName(envVars, func(n string) bool {
+               return strings.HasPrefix(n, "SOLR_SSL_")
+       })
+       assert.True(t, len(envVars) == 7)
+
+       expectedKeystorePath := "/mounted-tls-dir/keystore.p12"
+       expectedTruststorePath := "/mounted-tls-dir/truststore.p12"
+
+       for _, envVar := range envVars {
+               if envVar.Name == "SOLR_SSL_ENABLED" {
+                       assert.Equal(t, "true", envVar.Value)
+               }
+
+               if envVar.Name == "SOLR_SSL_KEY_STORE" {
+                       assert.Equal(t, expectedKeystorePath, envVar.Value)
+               }
+
+               if envVar.Name == "SOLR_SSL_TRUST_STORE" {
+                       assert.Equal(t, expectedTruststorePath, envVar.Value)
+               }
+
+               if envVar.Name == "SOLR_SSL_WANT_CLIENT_AUTH" {
+                       assert.Equal(t, "false", envVar.Value)
+               }
+
+               if envVar.Name == "SOLR_SSL_NEED_CLIENT_AUTH" {
+                       assert.Equal(t, "true", envVar.Value)
+               }
+
+               if envVar.Name == "SOLR_SSL_CLIENT_HOSTNAME_VERIFICATION" {
+                       assert.Equal(t, "true", envVar.Value)
+               }
+
+               if envVar.Name == "SOLR_SSL_CHECK_PEER_NAME" {
+                       assert.Equal(t, "true", envVar.Value)
+               }
+       }
+}
+
 // ensure the TLS related env vars are set for the Solr pod
 func expectTLSEnvVars(t *testing.T, envVars []corev1.EnvVar, 
expectedKeystorePasswordSecretName string, expectedKeystorePasswordSecretKey 
string, needsPkcs12InitContainer bool, expectedTruststorePath string) {
        assert.NotNil(t, envVars)
diff --git a/controllers/solrcloud_controller.go 
b/controllers/solrcloud_controller.go
index a7624a3..55c7b9d 100644
--- a/controllers/solrcloud_controller.go
+++ b/controllers/solrcloud_controller.go
@@ -367,7 +367,7 @@ func (r *SolrCloudReconciler) Reconcile(req ctrl.Request) 
(ctrl.Result, error) {
        tlsCertMd5 := ""
        needsPkcs12InitContainer := false // flag if the StatefulSet needs an 
additional initCont to create PKCS12 keystore
        // don't start reconciling TLS until we have ZK connectivity, avoids 
TLS code having to check for ZK
-       if !blockReconciliationOfStatefulSet && instance.Spec.SolrTLS != nil {
+       if !blockReconciliationOfStatefulSet && instance.Spec.SolrTLS != nil && 
instance.Spec.SolrTLS.PKCS12Secret != nil {
                foundTLSSecret, err := 
r.verifyTLSSecretConfig(instance.Spec.SolrTLS.PKCS12Secret.Name, 
instance.Namespace, instance.Spec.SolrTLS.KeyStorePasswordSecret)
                if err != nil {
                        return requeueOrNot, err
@@ -979,7 +979,7 @@ func (r *SolrCloudReconciler) indexAndWatchForTLSSecret(mgr 
ctrl.Manager, ctrlBu
        if err := mgr.GetFieldIndexer().IndexField(context.TODO(), 
&solr.SolrCloud{}, ".spec.solrTLS.pkcs12Secret", func(rawObj runtime.Object) 
[]string {
                // grab the SolrCloud object, extract the used configMap...
                solrCloud := rawObj.(*solr.SolrCloud)
-               if solrCloud.Spec.SolrTLS == nil {
+               if solrCloud.Spec.SolrTLS == nil || 
solrCloud.Spec.SolrTLS.PKCS12Secret == nil {
                        return nil
                }
                // ...and if so, return it
diff --git a/controllers/solrcloud_controller_tls_test.go 
b/controllers/solrcloud_controller_tls_test.go
index c6f6ad4..6afe170 100644
--- a/controllers/solrcloud_controller_tls_test.go
+++ b/controllers/solrcloud_controller_tls_test.go
@@ -121,6 +121,25 @@ func TestBasicAuthBootstrapWithTLS(t *testing.T) {
        verifyReconcileUserSuppliedTLS(t, instance, false, false)
 }
 
+func TestMountedTLSDir(t *testing.T) {
+       instance := buildTestSolrCloud()
+       mountedDir := &solr.MountedTLSDirectory{}
+       mountedDir.Path = "/mounted-tls-dir"
+       instance.Spec.SolrTLS = &solr.SolrTLSOptions{MountedServerTLSDir: 
mountedDir, CheckPeerName: true, ClientAuth: "Need", VerifyClientHostname: true}
+       expectMountedTLSDirEnvVars(t, util.TLSEnvVars(instance.Spec.SolrTLS, 
false))
+       verifyReconcileMountedTLSDir(t, instance)
+}
+
+func TestMountedTLSDirWithBasicAuth(t *testing.T) {
+       instance := buildTestSolrCloud()
+       mountedDir := &solr.MountedTLSDirectory{}
+       mountedDir.Path = "/mounted-tls-dir"
+       instance.Spec.SolrTLS = &solr.SolrTLSOptions{MountedServerTLSDir: 
mountedDir, CheckPeerName: true, ClientAuth: "Need", VerifyClientHostname: true}
+       instance.Spec.SolrSecurity = 
&solr.SolrSecurityOptions{AuthenticationType: solr.Basic} // with basic-auth too
+       expectMountedTLSDirEnvVars(t, util.TLSEnvVars(instance.Spec.SolrTLS, 
false))
+       verifyReconcileMountedTLSDir(t, instance)
+}
+
 // For TLS, all we really need is a secret holding the keystore password and a 
secret holding the pkcs12 keystore,
 // which can come from anywhere really, so this method tests handling of 
user-supplied secrets
 func TestUserSuppliedTLSSecretWithPkcs12Keystore(t *testing.T) {
@@ -233,6 +252,44 @@ func TestTLSSecretUpdate(t *testing.T) {
        verifyReconcileUserSuppliedTLS(t, instance, false, true)
 }
 
+func verifyReconcileMountedTLSDir(t *testing.T, instance *solr.SolrCloud) {
+       g := gomega.NewGomegaWithT(t)
+       ctx := context.TODO()
+
+       // Setup the Manager and Controller.  Wrap the Controller Reconcile 
function so it writes each request to a
+       // channel when it is finished.
+       mgr, err := manager.New(testCfg, manager.Options{})
+       g.Expect(err).NotTo(gomega.HaveOccurred())
+       testClient = mgr.GetClient()
+
+       solrCloudReconciler := &SolrCloudReconciler{
+               Client: testClient,
+               Log:    ctrl.Log.WithName("controllers").WithName("SolrCloud"),
+       }
+       newRec, requests := SetupTestReconcile(solrCloudReconciler)
+
+       g.Expect(solrCloudReconciler.SetupWithManagerAndReconciler(mgr, 
newRec)).NotTo(gomega.HaveOccurred())
+
+       stopMgr, mgrStopped := StartTestManager(mgr, g)
+
+       defer func() {
+               close(stopMgr)
+               mgrStopped.Wait()
+       }()
+
+       cleanupTest(g, instance.Namespace)
+
+       // now try to reconcile
+       err = testClient.Create(ctx, instance)
+       g.Expect(err).NotTo(gomega.HaveOccurred())
+       defer testClient.Delete(ctx, instance)
+
+       g.Eventually(requests, 
timeout).Should(gomega.Receive(gomega.Equal(expectedCloudWithTLSRequest)))
+       g.Eventually(requests, 
timeout).Should(gomega.Receive(gomega.Equal(expectedCloudWithTLSRequest)))
+
+       expectStatefulSetMountedTLSDirConfig(t, g, instance)
+}
+
 func verifyReconcileUserSuppliedTLS(t *testing.T, instance *solr.SolrCloud, 
needsPkcs12InitContainer bool, restartOnTLSSecretUpdate bool) {
        g := gomega.NewGomegaWithT(t)
        ctx := context.TODO()
@@ -335,6 +392,52 @@ func verifyReconcileUserSuppliedTLS(t *testing.T, instance 
*solr.SolrCloud, need
        }
 }
 
+func expectStatefulSetMountedTLSDirConfig(t *testing.T, g *gomega.GomegaWithT, 
sc *solr.SolrCloud) *appsv1.StatefulSet {
+       ctx := context.TODO()
+       stateful := &appsv1.StatefulSet{}
+       g.Eventually(func() error { return testClient.Get(ctx, 
expectedStatefulSetName, stateful) }, timeout).Should(gomega.Succeed())
+       podTemplate := &stateful.Spec.Template
+       expectMountedTLSDirConfigOnPodTemplate(t, podTemplate)
+
+       // Check HTTPS cluster prop setup container
+       assert.NotNil(t, podTemplate.Spec.InitContainers)
+       expectZkSetupInitContainerForTLS(t, sc, stateful)
+
+       // should have a mount for the initdb on main container
+       expectInitdbVolumeMount(t, podTemplate)
+
+       // verify initContainer to create initdb script to export the keystore 
& truststore passwords before launching the main container
+       name := "export-tls-password"
+       expInitContainer := expectInitContainer(t, podTemplate, name, "initdb", 
util.InitdbPath)
+       assert.Equal(t, 3, len(expInitContainer.Command), "Wrong command length 
for "+name+" init container")
+       assert.Contains(t, expInitContainer.Command[2], 
"SOLR_SSL_KEY_STORE_PASSWORD", "Wrong shell command for "+name+": 
"+expInitContainer.Command[2])
+       assert.Contains(t, expInitContainer.Command[2], 
"SOLR_SSL_TRUST_STORE_PASSWORD", "Wrong shell command for "+name+": 
"+expInitContainer.Command[2])
+
+       return stateful
+}
+
+func expectMountedTLSDirConfigOnPodTemplate(t *testing.T, podTemplate 
*corev1.PodTemplateSpec) {
+       assert.NotNil(t, podTemplate.Spec.Containers)
+       assert.True(t, len(podTemplate.Spec.Containers) > 0)
+       mainContainer := podTemplate.Spec.Containers[0]
+       assert.NotNil(t, mainContainer, "Didn't find the main solrcloud-node 
container in the sts!")
+       assert.NotNil(t, mainContainer.Env, "No Env vars for main 
solrcloud-node container in the sts!")
+       expectMountedTLSDirEnvVars(t, mainContainer.Env)
+
+       // verify the probes use a command with SSL opts
+       tlsJavaToolOpts := "-Djavax.net.ssl.keyStorePassword=$(cat 
/mounted-tls-dir/keystore-password) " +
+               "-Djavax.net.ssl.trustStorePassword=$(cat 
/mounted-tls-dir/keystore-password)"
+       tlsJavaSysProps := "-Djavax.net.ssl.keyStore=$SOLR_SSL_KEY_STORE 
-Djavax.net.ssl.trustStore=$SOLR_SSL_TRUST_STORE"
+       assert.NotNil(t, mainContainer.LivenessProbe, "main container should 
have a liveness probe defined")
+       assert.NotNil(t, mainContainer.LivenessProbe.Exec, "liveness probe 
should have an exec when mTLS is enabled")
+       assert.True(t, 
strings.Contains(mainContainer.LivenessProbe.Exec.Command[2], tlsJavaToolOpts), 
"liveness probe should invoke java with SSL opts")
+       assert.True(t, 
strings.Contains(mainContainer.LivenessProbe.Exec.Command[2], tlsJavaSysProps), 
"liveness probe should invoke java with SSL opts")
+       assert.NotNil(t, mainContainer.ReadinessProbe, "main container should 
have a readiness probe defined")
+       assert.NotNil(t, mainContainer.ReadinessProbe.Exec, "readiness probe 
should have an exec when mTLS is enabled")
+       assert.True(t, 
strings.Contains(mainContainer.ReadinessProbe.Exec.Command[2], 
tlsJavaToolOpts), "readiness probe should invoke java with SSL opts")
+       assert.True(t, 
strings.Contains(mainContainer.ReadinessProbe.Exec.Command[2], 
tlsJavaSysProps), "readiness probe should invoke java with SSL opts")
+}
+
 // ensures the TLS settings are applied correctly to the STS
 func expectStatefulSetTLSConfig(t *testing.T, g *gomega.GomegaWithT, sc 
*solr.SolrCloud, needsPkcs12InitContainer bool) *appsv1.StatefulSet {
        ctx := context.TODO()
@@ -345,7 +448,14 @@ func expectStatefulSetTLSConfig(t *testing.T, g 
*gomega.GomegaWithT, sc *solr.So
 
        // Check HTTPS cluster prop setup container
        assert.NotNil(t, podTemplate.Spec.InitContainers)
+       expectZkSetupInitContainerForTLS(t, sc, stateful)
+
+       return stateful
+}
+
+func expectZkSetupInitContainerForTLS(t *testing.T, sc *solr.SolrCloud, sts 
*appsv1.StatefulSet) {
        var zkSetupInitContainer *corev1.Container = nil
+       podTemplate := &sts.Spec.Template
        for _, cnt := range podTemplate.Spec.InitContainers {
                if cnt.Name == "setup-zk" {
                        zkSetupInitContainer = &cnt
@@ -356,7 +466,7 @@ func expectStatefulSetTLSConfig(t *testing.T, g 
*gomega.GomegaWithT, sc *solr.So
        if sc.Spec.SolrTLS != nil {
                assert.NotNil(t, zkSetupInitContainer, "Didn't find the 
zk-setup InitContainer in the sts!")
                if zkSetupInitContainer != nil {
-                       assert.Equal(t, 
stateful.Spec.Template.Spec.Containers[0].Image, zkSetupInitContainer.Image, 
"The zk-setup init container should use the same image as the Solr container")
+                       assert.Equal(t, 
sts.Spec.Template.Spec.Containers[0].Image, zkSetupInitContainer.Image, "The 
zk-setup init container should use the same image as the Solr container")
                        assert.Equal(t, 3, len(zkSetupInitContainer.Command), 
"Wrong command length for zk-setup init container")
                        assert.Contains(t, zkSetupInitContainer.Command[2], 
expCmd, "ZK Setup command does not set urlScheme")
                        expNumVars := 3
@@ -368,7 +478,6 @@ func expectStatefulSetTLSConfig(t *testing.T, g 
*gomega.GomegaWithT, sc *solr.So
        } else {
                assert.Nil(t, zkSetupInitContainer, "Shouldn't find the 
zk-setup InitContainer in the sts, when not using https!")
        }
-       return stateful
 }
 
 func verifyReconcileWithSecurity(t *testing.T, instance *solr.SolrCloud, 
deleteBootstrapSecret bool) {
@@ -519,12 +628,17 @@ func expectBasicAuthConfigOnPodTemplate(t *testing.T, 
instance *solr.SolrCloud,
                        "-Dsolr.install.dir=\"/opt/solr\" 
-Dlog4j.configurationFile=\"/opt/solr/server/resources/log4j2-console.xml\" " +
                        "-classpath 
\"/opt/solr/server/solr-webapp/webapp/WEB-INF/lib/*:/opt/solr/server/lib/ext/*:/opt/solr/server/lib/*\"
 " +
                        "org.apache.solr.util.SolrCLI api -get 
http://localhost:8983/solr/admin/info/system";
-               assert.NotNil(t, mainContainer.LivenessProbe, "main container 
should have a liveness probe defined")
-               assert.NotNil(t, mainContainer.LivenessProbe.Exec, "liveness 
probe should have an exec when auth is enabled")
-               assert.Equal(t, expProbeCmd, 
mainContainer.LivenessProbe.Exec.Command[2], "liveness probe should invoke java 
with auth opts")
-               assert.NotNil(t, mainContainer.ReadinessProbe, "main container 
should have a readiness probe defined")
-               assert.NotNil(t, mainContainer.ReadinessProbe.Exec, "readiness 
probe should have an exec when auth is enabled")
-               assert.Equal(t, expProbeCmd, 
mainContainer.ReadinessProbe.Exec.Command[2], "readiness probe should invoke 
java with auth opts")
+               if assert.NotNil(t, mainContainer.LivenessProbe, "main 
container should have a liveness probe defined") {
+                       if assert.NotNil(t, mainContainer.LivenessProbe.Exec, 
"liveness probe should have an exec when auth is enabled") {
+                               assert.Equal(t, expProbeCmd, 
mainContainer.LivenessProbe.Exec.Command[2], "liveness probe should invoke java 
with auth opts")
+                       }
+                       assert.EqualValues(t, 5, 
mainContainer.LivenessProbe.TimeoutSeconds, "liveness probe default timeout 
should be increased when using basicAuth")
+               }
+               if assert.NotNil(t, mainContainer.ReadinessProbe, "main 
container should have a readiness probe defined") {
+                       if assert.NotNil(t, mainContainer.ReadinessProbe.Exec, 
"readiness probe should have an exec when auth is enabled") {
+                               assert.Equal(t, expProbeCmd, 
mainContainer.ReadinessProbe.Exec.Command[2], "readiness probe should invoke 
java with auth opts")
+                       }
+               }
        }
 
        // if no user-provided auth secret, then check that security.json gets 
bootstrapped correctly
diff --git a/controllers/solrprometheusexporter_controller.go 
b/controllers/solrprometheusexporter_controller.go
index 9ba03a3..c3f4477 100644
--- a/controllers/solrprometheusexporter_controller.go
+++ b/controllers/solrprometheusexporter_controller.go
@@ -177,7 +177,7 @@ func (r *SolrPrometheusExporterReconciler) Reconcile(req 
ctrl.Request) (ctrl.Res
 
        // Make sure the TLS config is in order
        var tlsClientOptions *util.TLSClientOptions = nil
-       if prometheusExporter.Spec.SolrReference.SolrTLS != nil {
+       if prometheusExporter.Spec.SolrReference.SolrTLS != nil && 
prometheusExporter.Spec.SolrReference.SolrTLS.PKCS12Secret != nil {
                requeueOrNot := reconcile.Result{}
                ctx := context.TODO()
                foundTLSSecret := &corev1.Secret{}
@@ -216,6 +216,9 @@ func (r *SolrPrometheusExporterReconciler) Reconcile(req 
ctrl.Request) (ctrl.Res
                                }
                        }
                }
+       } else if prometheusExporter.Spec.SolrReference.SolrTLS != nil && 
prometheusExporter.Spec.SolrReference.SolrTLS.MountedServerTLSDir != nil {
+               tlsClientOptions = &util.TLSClientOptions{}
+               tlsClientOptions.TLSOptions = 
prometheusExporter.Spec.SolrReference.SolrTLS
        }
 
        basicAuthMd5 := ""
@@ -380,7 +383,7 @@ func (r *SolrPrometheusExporterReconciler) 
indexAndWatchForTLSSecret(mgr ctrl.Ma
        if err := mgr.GetFieldIndexer().IndexField(context.TODO(), 
&solrv1beta1.SolrPrometheusExporter{}, tlsSecretField, func(rawObj 
runtime.Object) []string {
                // grab the SolrCloud object, extract the referenced TLS 
secret...
                exporter := rawObj.(*solrv1beta1.SolrPrometheusExporter)
-               if exporter.Spec.SolrReference.SolrTLS == nil {
+               if exporter.Spec.SolrReference.SolrTLS == nil || 
exporter.Spec.SolrReference.SolrTLS.PKCS12Secret == nil {
                        return nil
                }
                // ...and if so, return it
diff --git a/controllers/solrprometheusexporter_controller_test.go 
b/controllers/solrprometheusexporter_controller_test.go
index be8f96e..2c35c87 100644
--- a/controllers/solrprometheusexporter_controller_test.go
+++ b/controllers/solrprometheusexporter_controller_test.go
@@ -927,3 +927,98 @@ func expectBasicAuthEnvVars(t *testing.T, envVars 
[]corev1.EnvVar, basicAuthSecr
 
        }
 }
+
+func TestMetricsReconcileWithMountedTLSDirConfig(t *testing.T) {
+
+       // ctx := context.TODO()
+
+       g := gomega.NewGomegaWithT(t)
+       instance := &solr.SolrPrometheusExporter{
+               ObjectMeta: metav1.ObjectMeta{Name: 
expectedMetricsRequest.Name, Namespace: expectedMetricsRequest.Namespace},
+               Spec:       solr.SolrPrometheusExporterSpec{},
+       }
+
+       // override the defaults here so we make sure the resulting 
initContainer uses them
+       instance.Spec.BusyBoxImage = &solr.ContainerImage{
+               Repository: "myBBImage",
+               Tag:        "test",
+               PullPolicy: "",
+       }
+
+       mountedDir := &solr.MountedTLSDirectory{}
+       mountedDir.Path = "/mounted-tls-dir"
+       instance.Spec.SolrReference.SolrTLS = 
&solr.SolrTLSOptions{MountedServerTLSDir: mountedDir, CheckPeerName: true, 
ClientAuth: "Need", VerifyClientHostname: true}
+
+       // Setup the Manager and Controller.  Wrap the Controller Reconcile 
function so it writes each request to a
+       // channel when it is finished.
+       mgr, err := manager.New(testCfg, manager.Options{})
+       g.Expect(err).NotTo(gomega.HaveOccurred())
+       testClient = mgr.GetClient()
+
+       solrPrometheusExporterReconciler := &SolrPrometheusExporterReconciler{
+               Client: testClient,
+               Log:    
ctrl.Log.WithName("controllers").WithName("SolrPrometheusExporter"),
+       }
+       newRec, requests := SetupTestReconcile(solrPrometheusExporterReconciler)
+       
g.Expect(solrPrometheusExporterReconciler.SetupWithManagerAndReconciler(mgr, 
newRec)).NotTo(gomega.HaveOccurred())
+
+       stopMgr, mgrStopped := StartTestManager(mgr, g)
+
+       defer func() {
+               close(stopMgr)
+               mgrStopped.Wait()
+       }()
+
+       cleanupTest(g, expectedMetricsRequest.Namespace)
+
+       // Create the SolrPrometheusExporter object and expect the Reconcile 
and Deployment to be created
+       err = testClient.Create(context.TODO(), instance)
+       // The instance object may not be a valid object because it might be 
missing some required fields.
+       // Please modify the instance object by adding required fields and then 
remove the following if statement.
+       if apierrors.IsInvalid(err) {
+               t.Logf("failed to create object, got an invalid object error: 
%v", err)
+               return
+       }
+       g.Expect(err).NotTo(gomega.HaveOccurred())
+       defer testClient.Delete(context.TODO(), instance)
+       g.Eventually(requests, 
timeout).Should(gomega.Receive(gomega.Equal(expectedMetricsRequest)))
+
+       deployment := expectDeployment(t, g, requests, expectedMetricsRequest, 
metricsDKey, "")
+
+       podTemplate := deployment.Spec.Template
+
+       // verify the mountedServerTLSDir config on the deployment
+       assert.NotNil(t, podTemplate.Spec.Containers)
+       assert.True(t, len(podTemplate.Spec.Containers) > 0)
+       mainContainer := podTemplate.Spec.Containers[0]
+       assert.NotNil(t, mainContainer, "Didn't find the main exporter 
container in the deployment!")
+       assert.NotNil(t, mainContainer.Env, "No Env vars for main exporter 
container in the deployment!")
+       // make sure JAVA_OPTS is set correctly with the TLS related sys props
+       envVars := filterVarsByName(mainContainer.Env, func(n string) bool {
+               return strings.HasPrefix(n, "JAVA_OPTS")
+       })
+       assert.Equal(t, 1, len(envVars))
+       javaOpts := envVars[0].Value
+       assert.True(t, strings.Contains(javaOpts, 
"-Djavax.net.ssl.keyStore=$(SOLR_SSL_KEY_STORE)"))
+       assert.True(t, strings.Contains(javaOpts, 
"-Djavax.net.ssl.trustStore=$(SOLR_SSL_TRUST_STORE)"))
+       assert.True(t, strings.Contains(javaOpts, 
"-Djavax.net.ssl.keyStoreType=PKCS12"))
+       assert.True(t, strings.Contains(javaOpts, 
"-Djavax.net.ssl.trustStoreType=PKCS12"))
+       assert.True(t, strings.Contains(javaOpts, 
"-Dsolr.ssl.checkPeerName=$(SOLR_SSL_CHECK_PEER_NAME)"))
+       assert.True(t, strings.Contains(javaOpts, 
"-Dsolr.jetty.ssl.verifyClientHostName=HTTPS"))
+       assert.False(t, strings.Contains(javaOpts, 
"-Djavax.net.ssl.keyStorePassword"))
+       assert.False(t, strings.Contains(javaOpts, 
"-Djavax.net.ssl.trustStorePassword"))
+
+       expInitContainer := expectInitContainerForMountedTLSDir(t, &podTemplate)
+       assert.Equal(t, "myBBImage:test", expInitContainer.Image)
+}
+
+func expectInitContainerForMountedTLSDir(t *testing.T, podTemplate 
*corev1.PodTemplateSpec) *corev1.Container {
+       // verify initContainer to create a wrapper script around the 
solr-exporter script
+       name := "create-tls-wrapper-script"
+       expInitContainer := expectInitContainer(t, podTemplate, name, 
"tls-wrapper-script", "/usr/local/solr-exporter-tls")
+       assert.Equal(t, 3, len(expInitContainer.Command), "Wrong command length 
for "+name+" init container")
+       assert.Contains(t, expInitContainer.Command[2], 
"-Djavax.net.ssl.keyStorePassword", "Wrong shell command for "+name+": 
"+expInitContainer.Command[2])
+       assert.Contains(t, expInitContainer.Command[2], 
"-Djavax.net.ssl.trustStorePassword", "Wrong shell command for "+name+": 
"+expInitContainer.Command[2])
+       assert.Contains(t, expInitContainer.Command[2], 
"/opt/solr/contrib/prometheus-exporter/bin/solr-exporter", "Wrong shell command 
for "+name+": "+expInitContainer.Command[2])
+       return expInitContainer
+}
diff --git a/controllers/util/prometheus_exporter_util.go 
b/controllers/util/prometheus_exporter_util.go
index 03636fb..b6249f6 100644
--- a/controllers/util/prometheus_exporter_util.go
+++ b/controllers/util/prometheus_exporter_util.go
@@ -18,6 +18,7 @@
 package util
 
 import (
+       "fmt"
        solr "github.com/apache/solr-operator/api/v1beta1"
        appsv1 "k8s.io/api/apps/v1"
        corev1 "k8s.io/api/core/v1"
@@ -169,11 +170,49 @@ func 
GenerateSolrPrometheusExporterDeployment(solrPrometheusExporter *solr.SolrP
                }
        }
 
+       var createTLSWrapperScriptInitContainer *corev1.Container
        if tls != nil {
                envVars = append(envVars, TLSEnvVars(tls.TLSOptions, 
tls.NeedsPkcs12InitContainer)...)
-               volumeMounts = append(volumeMounts, 
tlsVolumeMounts(tls.TLSOptions, tls.NeedsPkcs12InitContainer)...)
-               solrVolumes = append(solrVolumes, tlsVolumes(tls.TLSOptions, 
tls.NeedsPkcs12InitContainer)...)
                allJavaOpts = append(allJavaOpts, 
tlsJavaOpts(tls.TLSOptions)...)
+               if tls.TLSOptions.PKCS12Secret != nil {
+                       volumeMounts = append(volumeMounts, 
tlsVolumeMounts(tls.TLSOptions, tls.NeedsPkcs12InitContainer)...)
+                       solrVolumes = append(solrVolumes, 
tlsVolumes(tls.TLSOptions, tls.NeedsPkcs12InitContainer)...)
+               } else if tls.TLSOptions.MountedServerTLSDir != nil {
+                       volName := "tls-wrapper-script"
+                       mountPath := "/usr/local/solr-exporter-tls"
+                       wrapperScript := mountPath + 
"/launch-exporter-with-tls.sh"
+
+                       // the Prom exporter needs the keystore & truststore 
passwords in a Java system property, but the password
+                       // is stored in a file when using the mounted TLS dir 
approach, so we use a wrapper script around the main
+                       // container entry point to add these properties to 
JAVA_OPTS at runtime
+                       solrVolumes = append(solrVolumes, corev1.Volume{Name: 
volName, VolumeSource: corev1.VolumeSource{EmptyDir: 
&corev1.EmptyDirVolumeSource{}}})
+                       volumeMounts = append(volumeMounts, 
corev1.VolumeMount{Name: volName, MountPath: mountPath})
+
+                       /*
+                                 Create a wrapper script like:
+
+                                       #!/bin/bash
+                                       ksp=$(cat 
$MOUNTED_TLS_DIR/keystore-password)
+                                       tsp=$(cat 
$MOUNTED_TLS_DIR/keystore-password)
+                                       JAVA_OPTS="${JAVA_OPTS} 
-Djavax.net.ssl.keyStorePassword=${ksp} 
-Djavax.net.ssl.trustStorePassword=${tsp}"
+                                       
/opt/solr/contrib/prometheus-exporter/bin/solr-exporter $@
+
+                       */
+                       writeWrapperScript := fmt.Sprintf("cat << EOF > 
%s\n#!/bin/bash\nksp=\\$(cat %s)\ntsp=\\$(cat %s)\nJAVA_OPTS=\"\\${JAVA_OPTS} 
-Djavax.net.ssl.keyStorePassword=\\${ksp} 
-Djavax.net.ssl.trustStorePassword=\\${tsp}\"\n%s \\$@\nEOF\nchmod +x %s",
+                               wrapperScript, 
MountedTLSKeystorePasswordPath(tls.TLSOptions), 
MountedTLSTruststorePasswordPath(tls.TLSOptions), entrypoint, wrapperScript)
+
+                       c := solrPrometheusExporter.BusyBoxImage()
+                       createTLSWrapperScriptInitContainer = &corev1.Container{
+                               Name:            "create-tls-wrapper-script",
+                               Image:           c.ToImageName(),
+                               ImagePullPolicy: c.PullPolicy,
+                               Command:         []string{"sh", "-c", 
writeWrapperScript},
+                               VolumeMounts:    []corev1.VolumeMount{{Name: 
volName, MountPath: mountPath}},
+                       }
+
+                       // update the main container entrypoint for the main 
container to be our tls wrapper script
+                       entrypoint = wrapperScript
+               }
        }
 
        // basic auth enabled?
@@ -239,6 +278,10 @@ func 
GenerateSolrPrometheusExporterDeployment(solrPrometheusExporter *solr.SolrP
                initContainers = append(initContainers, pkcs12InitContainer)
        }
 
+       if createTLSWrapperScriptInitContainer != nil {
+               initContainers = append(initContainers, 
*createTLSWrapperScriptInitContainer)
+       }
+
        // track the MD5 of the custom exporter config in the pod spec 
annotations,
        // so we get a rolling restart when the configMap changes
        if configXmlMd5 != "" {
@@ -454,14 +497,18 @@ func CreateMetricsIngressRule(solrPrometheusExporter 
*solr.SolrPrometheusExporte
 func tlsJavaOpts(tlsOptions *solr.SolrTLSOptions) []string {
        javaOpts := []string{
                "-Djavax.net.ssl.keyStore=$(SOLR_SSL_KEY_STORE)",
-               
"-Djavax.net.ssl.keyStorePassword=$(SOLR_SSL_KEY_STORE_PASSWORD)",
                "-Djavax.net.ssl.trustStore=$(SOLR_SSL_TRUST_STORE)",
-               
"-Djavax.net.ssl.trustStorePassword=$(SOLR_SSL_TRUST_STORE_PASSWORD)",
                "-Djavax.net.ssl.keyStoreType=PKCS12",
                "-Djavax.net.ssl.trustStoreType=PKCS12",
                "-Dsolr.ssl.checkPeerName=$(SOLR_SSL_CHECK_PEER_NAME)",
        }
 
+       if tlsOptions.PKCS12Secret != nil {
+               // the password is available from env vars set from a secret
+               javaOpts = append(javaOpts, 
"-Djavax.net.ssl.keyStorePassword=$(SOLR_SSL_KEY_STORE_PASSWORD)")
+               javaOpts = append(javaOpts, 
"-Djavax.net.ssl.trustStorePassword=$(SOLR_SSL_TRUST_STORE_PASSWORD)")
+       } // else if mountedServerTLSDir != nil, the password is in a file and 
needs to be passed differently
+
        if tlsOptions.VerifyClientHostname {
                javaOpts = append(javaOpts, 
"-Dsolr.jetty.ssl.verifyClientHostName=HTTPS")
        }
diff --git a/controllers/util/solr_util.go b/controllers/util/solr_util.go
index db76ff9..b5a93e9 100644
--- a/controllers/util/solr_util.go
+++ b/controllers/util/solr_util.go
@@ -64,11 +64,13 @@ const (
        DefaultStatefulSetPodManagementPolicy = appsv1.ParallelPodManagement
 
        DefaultKeyStorePath         = "/var/solr/tls"
-       Pkcs12KeystoreFile          = "keystore.p12"
        DefaultWritableKeyStorePath = "/var/solr/tls/pkcs12"
        TLSCertKey                  = "tls.crt"
-       TLSKeyKey                   = "tls.key"
        DefaultTrustStorePath       = "/var/solr/tls-truststore"
+       InitdbPath                  = "/docker-entrypoint-initdb.d"
+       DefaultPkcs12KeystoreFile   = "keystore.p12"
+       DefaultPkcs12TruststoreFile = "truststore.p12"
+       DefaultKeystorePasswordFile = "keystore-password"
 )
 
 // GenerateStatefulSet returns a new appsv1.StatefulSet pointer generated for 
the SolrCloud instance
@@ -87,6 +89,7 @@ func GenerateStatefulSet(solrCloud *solr.SolrCloud, 
solrCloudStatus *solr.SolrCl
                probeScheme = corev1.URISchemeHTTPS
        }
 
+       defaultProbeTimeout := int32(1)
        defaultHandler := corev1.Handler{
                HTTPGet: &corev1.HTTPGetAction{
                        Scheme: probeScheme,
@@ -152,7 +155,7 @@ func GenerateStatefulSet(solrCloud *solr.SolrCloud, 
solrCloudStatus *solr.SolrCl
        solrDataVolumeName := "data"
        volumeMounts := []corev1.VolumeMount{{Name: solrDataVolumeName, 
MountPath: "/var/solr/data"}}
 
-       if solrCloud.Spec.SolrTLS != nil {
+       if solrCloud.Spec.SolrTLS != nil && 
solrCloud.Spec.SolrTLS.MountedServerTLSDir == nil {
                solrVolumes = append(solrVolumes, 
tlsVolumes(solrCloud.Spec.SolrTLS, createPkcs12InitContainer)...)
                volumeMounts = append(volumeMounts, 
tlsVolumeMounts(solrCloud.Spec.SolrTLS, createPkcs12InitContainer)...)
        }
@@ -243,6 +246,21 @@ func GenerateStatefulSet(solrCloud *solr.SolrCloud, 
solrCloudStatus *solr.SolrCl
                }
        }
 
+       // Auto-TLS uses an initContainer to create a script in the initdb, so 
mount that if it has not already been mounted
+       if solrCloud.Spec.SolrTLS != nil && 
solrCloud.Spec.SolrTLS.MountedServerTLSDir != nil {
+               var initdbMount *corev1.VolumeMount
+               for _, mount := range volumeMounts {
+                       if mount.MountPath == InitdbPath {
+                               initdbMount = &mount
+                               break
+                       }
+               }
+               if initdbMount == nil {
+                       solrVolumes = append(solrVolumes, corev1.Volume{Name: 
"initdb", VolumeSource: corev1.VolumeSource{EmptyDir: 
&corev1.EmptyDirVolumeSource{}}})
+                       volumeMounts = append(volumeMounts, 
corev1.VolumeMount{Name: "initdb", MountPath: InitdbPath})
+               }
+       }
+
        // Host Aliases
        hostAliases := make([]corev1.HostAlias, len(hostNameIPs))
        if len(hostAliases) == 0 {
@@ -378,6 +396,7 @@ func GenerateStatefulSet(solrCloud *solr.SolrCloud, 
solrCloudStatus *solr.SolrCl
                }
                // reset the defaultHandler for the probes to invoke the 
SolrCLI api action instead of HTTP
                defaultHandler = corev1.Handler{Exec: 
&corev1.ExecAction{Command: []string{"sh", "-c", probeCommand}}}
+               defaultProbeTimeout = 5
        }
 
        // track the MD5 of the custom solr.xml in the pod spec annotations,
@@ -428,7 +447,7 @@ func GenerateStatefulSet(solrCloud *solr.SolrCloud, 
solrCloudStatus *solr.SolrCl
                        },
                        LivenessProbe: &corev1.Probe{
                                InitialDelaySeconds: 20,
-                               TimeoutSeconds:      1,
+                               TimeoutSeconds:      defaultProbeTimeout,
                                SuccessThreshold:    1,
                                FailureThreshold:    3,
                                PeriodSeconds:       10,
@@ -436,7 +455,7 @@ func GenerateStatefulSet(solrCloud *solr.SolrCloud, 
solrCloudStatus *solr.SolrCl
                        },
                        ReadinessProbe: &corev1.Probe{
                                InitialDelaySeconds: 15,
-                               TimeoutSeconds:      1,
+                               TimeoutSeconds:      defaultProbeTimeout,
                                SuccessThreshold:    1,
                                FailureThreshold:    3,
                                PeriodSeconds:       5,
@@ -626,6 +645,10 @@ func generateSolrSetupInitContainers(solrCloud 
*solr.SolrCloud, solrCloudStatus
                containers = append(containers, zkSetupContainer)
        }
 
+       if solrCloud.Spec.SolrTLS != nil && 
solrCloud.Spec.SolrTLS.MountedServerTLSDir != nil {
+               containers = append(containers, 
generateTLSInitdbScriptInitContainer(solrCloud))
+       }
+
        return containers
 }
 
@@ -825,7 +848,7 @@ func GenerateIngress(solrCloud *solr.SolrCloud, nodeNames 
[]string) (ingress *ne
        rules := CreateSolrIngressRules(solrCloud, nodeNames, 
append([]string{extOpts.DomainName}, extOpts.AdditionalDomainNames...))
 
        var ingressTLS []netv1.IngressTLS
-       if solrCloud.Spec.SolrTLS != nil {
+       if solrCloud.Spec.SolrTLS != nil && solrCloud.Spec.SolrTLS.PKCS12Secret 
!= nil {
                if annotations == nil {
                        annotations = make(map[string]string, 1)
                }
@@ -834,7 +857,7 @@ func GenerateIngress(solrCloud *solr.SolrCloud, nodeNames 
[]string) (ingress *ne
                        
annotations["nginx.ingress.kubernetes.io/backend-protocol"] = "HTTPS"
                }
                ingressTLS = append(ingressTLS, netv1.IngressTLS{SecretName: 
solrCloud.Spec.SolrTLS.PKCS12Secret.Name})
-       }
+       } // else if using mountedServerTLSDir, it's likely they'll have an 
auto-wired TLS solution for Ingress as well via annotations
 
        ingress = &netv1.Ingress{
                ObjectMeta: metav1.ObjectMeta{
@@ -921,6 +944,30 @@ func CreateNodeIngressRule(solrCloud *solr.SolrCloud, 
nodeName string, domainNam
        return ingressRule
 }
 
+func generateTLSInitdbScriptInitContainer(solrCloud *solr.SolrCloud) 
corev1.Container {
+       // run an initContainer that creates a script in the initdb that 
exports the
+       // keystore secret from a file to the env before Solr is started
+       shCmd := fmt.Sprintf("echo -e \"#!/bin/bash\\nexport 
SOLR_SSL_KEY_STORE_PASSWORD=\\`cat %s\\`\\nexport 
SOLR_SSL_TRUST_STORE_PASSWORD=\\`cat %s\\`\" > 
/docker-entrypoint-initdb.d/export-tls-vars.sh",
+               MountedTLSKeystorePasswordPath(solrCloud.Spec.SolrTLS), 
MountedTLSTruststorePasswordPath(solrCloud.Spec.SolrTLS))
+
+       /*
+          Init container creates a script like:
+
+             #!/bin/bash
+             export SOLR_SSL_KEY_STORE_PASSWORD=`cat 
$MOUNTED_TLS_DIR/keystore-password`
+             export SOLR_SSL_TRUST_STORE_PASSWORD=`cat 
$MOUNTED_TLS_DIR/keystore-password`
+
+       */
+
+       return corev1.Container{
+               Name:            "export-tls-password",
+               Image:           solrCloud.Spec.BusyBoxImage.ToImageName(),
+               ImagePullPolicy: solrCloud.Spec.BusyBoxImage.PullPolicy,
+               Command:         []string{"sh", "-c", shCmd},
+               VolumeMounts:    []corev1.VolumeMount{{Name: "initdb", 
MountPath: InitdbPath}},
+       }
+}
+
 // TODO: Have this replace the postStart hook for creating the chroot
 func generateZKInteractionInitContainer(solrCloud *solr.SolrCloud, 
solrCloudStatus *solr.SolrCloudStatus, reconcileConfigInfo map[string]string) 
(bool, corev1.Container) {
        allSolrOpts := make([]string, 0)
@@ -990,33 +1037,43 @@ func TLSEnvVars(opts *solr.SolrTLSOptions, 
createPkcs12InitContainer bool) []cor
                wantClientAuth = "true"
        }
 
-       // the keystore path depends on whether we're just loading it from the 
secret or whether
-       // our initContainer has to generate it from the TLS secret using 
openssl
-       // this complexity is due to the secret mount directory not being 
writable
-       var keystorePath string
-       if createPkcs12InitContainer {
-               keystorePath = DefaultWritableKeyStorePath
+       var keystoreFile string
+       var passwordValueFrom *corev1.EnvVarSource
+       var truststoreFile string
+       var truststorePassFrom *corev1.EnvVarSource
+       if opts.MountedServerTLSDir != nil {
+               // TLS files are mounted by some external agent
+               keystoreFile = mountedTLSKeystorePath(opts)
+               truststoreFile = mountedTLSTruststorePath(opts)
        } else {
-               keystorePath = DefaultKeyStorePath
-       }
-
-       keystoreFile := keystorePath + "/" + Pkcs12KeystoreFile
-       passwordValueFrom := &corev1.EnvVarSource{SecretKeyRef: 
opts.KeyStorePasswordSecret}
-
-       // If using a truststore that is different from the keystore
-       truststoreFile := keystoreFile
-       truststorePassFrom := passwordValueFrom
-       if opts.TrustStoreSecret != nil {
-               if opts.TrustStoreSecret.Name != opts.PKCS12Secret.Name {
-                       // trust store is in a different secret, so will be 
mounted in a different dir
-                       truststoreFile = DefaultTrustStorePath
+               // the keystore path depends on whether we're just loading it 
from the secret or whether
+               // our initContainer has to generate it from the TLS secret 
using openssl
+               // this complexity is due to the secret mount directory not 
being writable
+               var keystorePath string
+               if createPkcs12InitContainer {
+                       keystorePath = DefaultWritableKeyStorePath
                } else {
-                       // trust store is a different key in the same secret as 
the keystore
-                       truststoreFile = DefaultKeyStorePath
+                       keystorePath = DefaultKeyStorePath
                }
-               truststoreFile += "/" + opts.TrustStoreSecret.Key
-               if opts.TrustStorePasswordSecret != nil {
-                       truststorePassFrom = &corev1.EnvVarSource{SecretKeyRef: 
opts.TrustStorePasswordSecret}
+
+               keystoreFile = keystorePath + "/" + DefaultPkcs12KeystoreFile
+               passwordValueFrom = &corev1.EnvVarSource{SecretKeyRef: 
opts.KeyStorePasswordSecret}
+
+               // If using a truststore that is different from the keystore
+               truststoreFile = keystoreFile
+               truststorePassFrom = passwordValueFrom
+               if opts.TrustStoreSecret != nil {
+                       if opts.TrustStoreSecret.Name != opts.PKCS12Secret.Name 
{
+                               // trust store is in a different secret, so 
will be mounted in a different dir
+                               truststoreFile = DefaultTrustStorePath
+                       } else {
+                               // trust store is a different key in the same 
secret as the keystore
+                               truststoreFile = DefaultKeyStorePath
+                       }
+                       truststoreFile += "/" + opts.TrustStoreSecret.Key
+                       if opts.TrustStorePasswordSecret != nil {
+                               truststorePassFrom = 
&corev1.EnvVarSource{SecretKeyRef: opts.TrustStorePasswordSecret}
+                       }
                }
        }
 
@@ -1030,18 +1087,10 @@ func TLSEnvVars(opts *solr.SolrTLSOptions, 
createPkcs12InitContainer bool) []cor
                        Value: keystoreFile,
                },
                {
-                       Name:      "SOLR_SSL_KEY_STORE_PASSWORD",
-                       ValueFrom: passwordValueFrom,
-               },
-               {
                        Name:  "SOLR_SSL_TRUST_STORE",
                        Value: truststoreFile,
                },
                {
-                       Name:      "SOLR_SSL_TRUST_STORE_PASSWORD",
-                       ValueFrom: truststorePassFrom,
-               },
-               {
                        Name:  "SOLR_SSL_WANT_CLIENT_AUTH",
                        Value: wantClientAuth,
                },
@@ -1059,6 +1108,14 @@ func TLSEnvVars(opts *solr.SolrTLSOptions, 
createPkcs12InitContainer bool) []cor
                },
        }
 
+       if passwordValueFrom != nil {
+               envVars = append(envVars, corev1.EnvVar{Name: 
"SOLR_SSL_KEY_STORE_PASSWORD", ValueFrom: passwordValueFrom})
+       }
+
+       if truststorePassFrom != nil {
+               envVars = append(envVars, corev1.EnvVar{Name: 
"SOLR_SSL_TRUST_STORE_PASSWORD", ValueFrom: truststorePassFrom})
+       }
+
        return envVars
 }
 
@@ -1148,7 +1205,7 @@ func generatePkcs12InitContainer(opts 
*solr.SolrTLSOptions, imageName string, im
 
        cmd := "openssl pkcs12 -export -in " + DefaultKeyStorePath + "/" + 
TLSCertKey + " -in " + DefaultKeyStorePath +
                "/ca.crt -inkey " + DefaultKeyStorePath + "/tls.key -out " + 
DefaultKeyStorePath +
-               "/pkcs12/" + Pkcs12KeystoreFile + " -passout 
pass:${SOLR_SSL_KEY_STORE_PASSWORD}"
+               "/pkcs12/" + DefaultPkcs12KeystoreFile + " -passout 
pass:${SOLR_SSL_KEY_STORE_PASSWORD}"
        return corev1.Container{
                Name:                     "gen-pkcs12-keystore",
                Image:                    imageName,
@@ -1445,7 +1502,7 @@ func configureSecureProbeCommand(solrCloud 
*solr.SolrCloud, defaultProbeGetActio
        enableBasicAuth := ""
        var volMount *corev1.VolumeMount
        var vol *corev1.Volume
-       if solrCloud.Spec.SolrSecurity != nil {
+       if solrCloud.Spec.SolrSecurity != nil && 
solrCloud.Spec.SolrSecurity.ProbesRequireAuth {
                secretName := solrCloud.BasicAuthSecretName()
                defaultMode := int32(420)
                vol = &corev1.Volume{
@@ -1466,24 +1523,80 @@ func configureSecureProbeCommand(solrCloud 
*solr.SolrCloud, defaultProbeGetActio
        }
 
        // Is TLS enabled? If so we need some additional SSL related props
-       tlsProps := ""
-       if solrCloud.Spec.SolrTLS != nil {
-               tlsProps = "-Djavax.net.ssl.keyStore=$SOLR_SSL_KEY_STORE 
-Djavax.net.ssl.keyStorePassword=$SOLR_SSL_KEY_STORE_PASSWORD " +
-                       "-Djavax.net.ssl.trustStore=$SOLR_SSL_TRUST_STORE 
-Djavax.net.ssl.trustStorePassword=$SOLR_SSL_TRUST_STORE_PASSWORD"
-       }
-
-       javaToolOptions := strings.TrimSpace(basicAuthOption + " " + tlsProps)
+       tlsJavaToolOpts, tlsJavaSysProps := 
secureProbeTLSJavaToolOpts(solrCloud.Spec.SolrTLS)
+       javaToolOptions := strings.TrimSpace(basicAuthOption + " " + 
tlsJavaToolOpts)
 
        // construct the probe command to invoke the SolrCLI "api" action
        //
        // and yes, this is ugly, but bin/solr doesn't expose the "api" action 
(as of 8.8.0) so we have to invoke java directly
        // taking some liberties on the /opt/solr path based on the official 
Docker image as there is no ENV var set for that path
-       probeCommand := fmt.Sprintf("JAVA_TOOL_OPTIONS=\"%s\" java 
-Dsolr.ssl.checkPeerName=false %s "+
+       probeCommand := fmt.Sprintf("JAVA_TOOL_OPTIONS=\"%s\" java %s 
-Dsolr.ssl.checkPeerName=false %s "+
                "-Dsolr.install.dir=\"/opt/solr\" 
-Dlog4j.configurationFile=\"/opt/solr/server/resources/log4j2-console.xml\" "+
                "-classpath 
\"/opt/solr/server/solr-webapp/webapp/WEB-INF/lib/*:/opt/solr/server/lib/ext/*:/opt/solr/server/lib/*\"
 "+
                "org.apache.solr.util.SolrCLI api -get %s://localhost:%d%s",
-               javaToolOptions, enableBasicAuth, solrCloud.UrlScheme(), 
defaultProbeGetAction.Port.IntVal, defaultProbeGetAction.Path)
+               javaToolOptions, tlsJavaSysProps, enableBasicAuth, 
solrCloud.UrlScheme(), defaultProbeGetAction.Port.IntVal, 
defaultProbeGetAction.Path)
        probeCommand = 
regexp.MustCompile(`\s+`).ReplaceAllString(strings.TrimSpace(probeCommand), " ")
 
        return probeCommand, vol, volMount
 }
+
+func secureProbeTLSJavaToolOpts(tls *solr.SolrTLSOptions) (string, string) {
+       tlsJavaToolOpts := ""
+       tlsJavaSysProps := ""
+       if tls != nil {
+               tlsJavaSysProps = "-Djavax.net.ssl.keyStore=$SOLR_SSL_KEY_STORE 
-Djavax.net.ssl.trustStore=$SOLR_SSL_TRUST_STORE"
+
+               // If the keystore passwords are in a file, then we need to cat 
the file(s) into JAVA_TOOL_OPTIONS
+               if tls.MountedServerTLSDir != nil {
+                       tlsJavaToolOpts += " 
-Djavax.net.ssl.keyStorePassword=$(cat " + MountedTLSKeystorePasswordPath(tls) 
+ ")"
+                       tlsJavaToolOpts += " 
-Djavax.net.ssl.trustStorePassword=$(cat " + 
MountedTLSTruststorePasswordPath(tls) + ")"
+               } else {
+                       tlsJavaSysProps += " 
-Djavax.net.ssl.keyStorePassword=$SOLR_SSL_KEY_STORE_PASSWORD"
+                       tlsJavaSysProps += " 
-Djavax.net.ssl.trustStorePassword=$SOLR_SSL_TRUST_STORE_PASSWORD"
+               }
+       }
+       return tlsJavaToolOpts, tlsJavaSysProps
+}
+
+func mountedTLSKeystorePath(tls *solr.SolrTLSOptions) string {
+       path := ""
+       if tls.MountedServerTLSDir != nil {
+               path = mountedTLSPath(tls.MountedServerTLSDir, 
tls.MountedServerTLSDir.KeystoreFile, DefaultPkcs12KeystoreFile)
+       }
+       return path
+}
+
+func MountedTLSKeystorePasswordPath(tls *solr.SolrTLSOptions) string {
+       path := ""
+       if tls.MountedServerTLSDir != nil {
+               path = mountedTLSPath(tls.MountedServerTLSDir, 
tls.MountedServerTLSDir.KeystorePasswordFile, DefaultKeystorePasswordFile)
+       }
+       return path
+}
+
+func mountedTLSTruststorePath(tls *solr.SolrTLSOptions) string {
+       path := ""
+       if tls.MountedServerTLSDir != nil {
+               path = mountedTLSPath(tls.MountedServerTLSDir, 
tls.MountedServerTLSDir.TruststoreFile, DefaultPkcs12TruststoreFile)
+       }
+       return path
+}
+
+func MountedTLSTruststorePasswordPath(tls *solr.SolrTLSOptions) string {
+       path := ""
+       if tls.MountedServerTLSDir != nil {
+               if tls.MountedServerTLSDir.TruststorePasswordFile != "" {
+                       path = mountedTLSPath(tls.MountedServerTLSDir, 
tls.MountedServerTLSDir.TruststorePasswordFile, "")
+               } else {
+                       path = MountedTLSKeystorePasswordPath(tls)
+               }
+       }
+       return path
+}
+
+func mountedTLSPath(dir *solr.MountedTLSDirectory, fileName string, 
defaultName string) string {
+       if fileName == "" {
+               fileName = defaultName
+       }
+       return fmt.Sprintf("%s/%s", dir.Path, fileName)
+}
diff --git a/docs/solr-cloud/solr-cloud-crd.md 
b/docs/solr-cloud/solr-cloud-crd.md
index 9d6a73e..a5c3cef 100644
--- a/docs/solr-cloud/solr-cloud-crd.md
+++ b/docs/solr-cloud/solr-cloud-crd.md
@@ -313,8 +313,9 @@ The Solr operator provides **optional** configuration 
settings to enable TLS for
 
 Enabling TLS for Solr is a straight-forward process once you have a [**PKCS12 
keystore**]((https://en.wikipedia.org/wiki/PKCS_12)) containing an 
[X.509](https://en.wikipedia.org/wiki/X.509) certificate and private key; as of 
Java 8, PKCS12 is the default keystore format supported by the JVM.
 
-There are two basic use cases supported by the Solr operator. First, you can 
use cert-manager to issue a certificate and store the resulting PKCS12 keystore 
in a Kubernetes TLS secret. 
+There are three basic use cases supported by the Solr operator. First, you can 
use cert-manager to issue a certificate and store the resulting PKCS12 keystore 
in a Kubernetes TLS secret. 
 Alternatively, you can create the TLS secret manually from a certificate 
obtained by some other means. In both cases, you simply point your SolrCloud 
CRD to the resulting TLS secret and corresponding keystore password secret.
+Lastly, as of **v0.4.0**, you can supply the path to a directory containing 
TLS files that are mounted by some external agent or CSI driver.  
 
 ### Use cert-manager to issue the certificate
 
@@ -519,6 +520,18 @@ spec:
 ``` 
 _Tip: if your truststore is not in PKCS12 format, use `openssl` to convert 
it._ 
 
+### Mounted TLS Directory
+_Since v0.4.0_
+
+The options discussed to this point require that all Solr pods share the same 
certificate and truststore. An emerging pattern in the Kubernetes ecosystem is 
to issue a unique certificate for each pod.
+Typically this operation is performed by an external agent, such as a 
cert-manager extension, that uses mutating webhooks to mount a unique 
certificate and supporting files on each pod dynamically.
+How the pod-specific certificates get issued is beyond the scope of the Solr 
operator. Under this scheme, you can use 
`spec.solrTLS.mountedServerTLSDir.path` to specify the path where the TLS files 
are mounted on the main pod.
+Given the `spec.solrTLS.mountedServerTLSDir.path`, the Solr operator will 
enable TLS on Solr using the keystore and truststore files in this directory.
+
+When using the mounted TLS directory option, you need to ensure each Solr pod 
gets restarted before the certificate expires. Solr does not support hot 
reloading of the keystore or truststore.
+Consequently, we recommend using the `spec.updateStrategy.restartSchedule` to 
restart pods before the certificate expires. 
+Typically, with this scheme, a new certificate is issued whenever a pod is 
restarted.
+
 ### Ingress
 
 The Solr operator may create an Ingress for exposing Solr pods externally. 
When TLS is enabled, the operator adds the following annotation and TLS 
settings to the Ingress manifest, such as:
diff --git a/docs/solr-prometheus-exporter/README.md 
b/docs/solr-prometheus-exporter/README.md
index f27e20c..4dca7b2 100644
--- a/docs/solr-prometheus-exporter/README.md
+++ b/docs/solr-prometheus-exporter/README.md
@@ -76,6 +76,11 @@ spec:
 
 **This only applies to the SolrJ client the exporter uses to make requests to 
your TLS-enabled Solr pods and does not enable HTTPS for the exporter service.**
 
+#### Mounted TLS Directory
+_Since v0.4.0_
+
+You can use the `spec.solrReference.solrTLS.mountedServerTLSDir.path` to point 
to a directory containing certificate files mounted by an external agent or CSI 
driver.
+
 ### Prometheus Exporter with Basic Auth
 _Since v0.3.0_
 
diff --git a/helm/solr-operator/Chart.yaml b/helm/solr-operator/Chart.yaml
index 68f35ee..e4cd341 100644
--- a/helm/solr-operator/Chart.yaml
+++ b/helm/solr-operator/Chart.yaml
@@ -119,6 +119,13 @@ annotations:
           url: https://github.com/apache/solr-operator/issues/289
         - name: Github PR
           url: https://github.com/apache/solr-operator/pull/299
+    - kind: security
+      description: Add a mountedServerTLSDir config option to support a unique 
certificate per pod mounted dynamically by an external agent or CSI driver
+      links:
+        - name: Github Issue
+          url: https://github.com/apache/solr-operator/issues/291
+        - name: Github PR
+          url: https://github.com/apache/solr-operator/pull/292
   artifacthub.io/images: |
     - name: solr-operator
       image: apache/solr-operator:v0.4.0-prerelease
diff --git a/helm/solr-operator/crds/crds.yaml 
b/helm/solr-operator/crds/crds.yaml
index 3e53943..b2feddb 100644
--- a/helm/solr-operator/crds/crds.yaml
+++ b/helm/solr-operator/crds/crds.yaml
@@ -5703,7 +5703,7 @@ spec:
                     - Need
                     type: string
                   keyStorePasswordSecret:
-                    description: Secret containing the key store password; 
this field is required as most JVMs do not support pkcs12 keystores without a 
password
+                    description: Secret containing the key store password; 
this field is required unless mountedServerTLSDir is used, as most JVMs do not 
support pkcs12 keystores without a password
                     properties:
                       key:
                         description: The key of the secret to select from.  
Must be a valid secret key.
@@ -5717,8 +5717,29 @@ spec:
                     required:
                     - key
                     type: object
+                  mountedServerTLSDir:
+                    description: Used to specify a path where the keystore, 
truststore, and password files for the server certificate are mounted by an 
external agent or CSI driver. This option is typically used with 
`spec.updateStrategy.restartSchedule` to restart Solr pods before the mounted 
TLS cert expires.
+                    properties:
+                      keystoreFile:
+                        description: Override the name of the keystore file; 
defaults to keystore.p12
+                        type: string
+                      keystorePasswordFile:
+                        description: Override the name of the keystore 
password file; defaults to keystore-password
+                        type: string
+                      path:
+                        description: The path on the main Solr container where 
the TLS files are mounted by some external agent or CSI Driver
+                        type: string
+                      truststoreFile:
+                        description: Override the name of the truststore file; 
defaults truststore.p12 To use the same file as the keystore, override this 
variable with the name of your keystore file
+                        type: string
+                      truststorePasswordFile:
+                        description: Override the name of the truststore 
password file; defaults to the same value as the KeystorePasswordFile
+                        type: string
+                    required:
+                    - path
+                    type: object
                   pkcs12Secret:
-                    description: TLS Secret containing a pkcs12 keystore
+                    description: TLS Secret containing a pkcs12 keystore; 
required unless mountedServerTLSDir is used
                     properties:
                       key:
                         description: The key of the secret to select from.  
Must be a valid secret key.
@@ -5768,9 +5789,6 @@ spec:
                   verifyClientHostname:
                     description: Verify client's hostname during SSL handshake
                     type: boolean
-                required:
-                - keyStorePasswordSecret
-                - pkcs12Secret
                 type: object
               updateStrategy:
                 description: Define how Solr rolling updates are executed.
@@ -6693,6 +6711,19 @@ spec:
           spec:
             description: SolrPrometheusExporterSpec defines the desired state 
of SolrPrometheusExporter
             properties:
+              busyBoxImage:
+                description: An initContainer is needed to create a wrapper 
script around the exporter entrypoint when TLS is enabled with the 
`spec.solrReference.solrTLS.mountedServerTLSDir` option
+                properties:
+                  imagePullSecret:
+                    type: string
+                  pullPolicy:
+                    description: PullPolicy describes a policy for if/when to 
pull a container image
+                    type: string
+                  repository:
+                    type: string
+                  tag:
+                    type: string
+                type: object
               customKubeOptions:
                 description: Provide custom options for kubernetes objects 
created for the SolrPrometheusExporter.
                 properties:
@@ -10078,7 +10109,7 @@ spec:
                         - Need
                         type: string
                       keyStorePasswordSecret:
-                        description: Secret containing the key store password; 
this field is required as most JVMs do not support pkcs12 keystores without a 
password
+                        description: Secret containing the key store password; 
this field is required unless mountedServerTLSDir is used, as most JVMs do not 
support pkcs12 keystores without a password
                         properties:
                           key:
                             description: The key of the secret to select from. 
 Must be a valid secret key.
@@ -10092,8 +10123,29 @@ spec:
                         required:
                         - key
                         type: object
+                      mountedServerTLSDir:
+                        description: Used to specify a path where the 
keystore, truststore, and password files for the server certificate are mounted 
by an external agent or CSI driver. This option is typically used with 
`spec.updateStrategy.restartSchedule` to restart Solr pods before the mounted 
TLS cert expires.
+                        properties:
+                          keystoreFile:
+                            description: Override the name of the keystore 
file; defaults to keystore.p12
+                            type: string
+                          keystorePasswordFile:
+                            description: Override the name of the keystore 
password file; defaults to keystore-password
+                            type: string
+                          path:
+                            description: The path on the main Solr container 
where the TLS files are mounted by some external agent or CSI Driver
+                            type: string
+                          truststoreFile:
+                            description: Override the name of the truststore 
file; defaults truststore.p12 To use the same file as the keystore, override 
this variable with the name of your keystore file
+                            type: string
+                          truststorePasswordFile:
+                            description: Override the name of the truststore 
password file; defaults to the same value as the KeystorePasswordFile
+                            type: string
+                        required:
+                        - path
+                        type: object
                       pkcs12Secret:
-                        description: TLS Secret containing a pkcs12 keystore
+                        description: TLS Secret containing a pkcs12 keystore; 
required unless mountedServerTLSDir is used
                         properties:
                           key:
                             description: The key of the secret to select from. 
 Must be a valid secret key.
@@ -10143,9 +10195,6 @@ spec:
                       verifyClientHostname:
                         description: Verify client's hostname during SSL 
handshake
                         type: boolean
-                    required:
-                    - keyStorePasswordSecret
-                    - pkcs12Secret
                     type: object
                   standalone:
                     description: Reference of a standalone solr instance

Reply via email to