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 75bdb0d  Refactor security related code into a separate 
solr_security_util.go vs. sprinkled throughout the codebase. (#334)
75bdb0d is described below

commit 75bdb0dc05e80fd362e7c2e04fb18faf30f395f6
Author: Timothy Potter <[email protected]>
AuthorDate: Wed Oct 6 10:25:28 2021 -0600

    Refactor security related code into a separate solr_security_util.go vs. 
sprinkled throughout the codebase. (#334)
---
 controllers/solrcloud_controller.go          |  91 +-----
 controllers/solrcloud_controller_tls_test.go |   3 +
 controllers/util/prometheus_exporter_util.go |   6 +-
 controllers/util/solr_security_util.go       | 433 +++++++++++++++++++++++++++
 controllers/util/solr_tls_util.go            |   3 +-
 controllers/util/solr_util.go                | 293 ++----------------
 6 files changed, 468 insertions(+), 361 deletions(-)

diff --git a/controllers/solrcloud_controller.go 
b/controllers/solrcloud_controller.go
index cc27f62..19df029 100644
--- a/controllers/solrcloud_controller.go
+++ b/controllers/solrcloud_controller.go
@@ -279,86 +279,13 @@ func (r *SolrCloudReconciler) Reconcile(ctx 
context.Context, req ctrl.Request) (
                }
        }
 
-       basicAuthHeader := ""
+       // Holds security config info needed during construction of the 
StatefulSet
+       var security *util.SecurityConfig = nil
        if instance.Spec.SolrSecurity != nil {
-               sec := instance.Spec.SolrSecurity
-
-               if sec.AuthenticationType != solrv1beta1.Basic {
-                       return requeueOrNot, fmt.Errorf("%s not supported! Only 
'Basic' authentication is supported by the Solr operator",
-                               instance.Spec.SolrSecurity.AuthenticationType)
-               }
-
-               // for now, we don't support 
'solrSecurity.probesRequireAuth=true' and custom probe paths,
-               // so make the user fix that so there are no surprises later
-               if sec.ProbesRequireAuth && 
instance.Spec.CustomSolrKubeOptions.PodOptions != nil {
-                       for _, path := range util.GetCustomProbePaths(instance) 
{
-                               if path != util.DefaultProbePath {
-                                       return requeueOrNot, fmt.Errorf(
-                                               "custom probe path %s not 
supported when 'solrSecurity.probesRequireAuth=true'; must use 
'solrSecurity.probesRequireAuth=false' when using custom probe endpoints", path)
-                               }
-                       }
-               }
-
-               basicAuthSecret := &corev1.Secret{}
-
-               // user has the option of providing a secret with credentials 
the operator should use to make requests to Solr
-               if sec.BasicAuthSecret != "" {
-                       if err := r.Get(ctx, types.NamespacedName{Name: 
sec.BasicAuthSecret, Namespace: instance.Namespace}, basicAuthSecret); err != 
nil {
-                               return requeueOrNot, err
-                       }
-
-                       err = util.ValidateBasicAuthSecret(basicAuthSecret)
-                       if err != nil {
-                               return requeueOrNot, err
-                       }
-
-               } else {
-                       // We're supplying a secret with random passwords and a 
default security.json
-                       // since we randomly generate the passwords, we need to 
lookup the secret first and only create if not exist
-                       err = r.Get(ctx, types.NamespacedName{Name: 
instance.BasicAuthSecretName(), Namespace: instance.Namespace}, basicAuthSecret)
-                       if err != nil && errors.IsNotFound(err) {
-                               authSecret, bootstrapSecret := 
util.GenerateBasicAuthSecretWithBootstrap(instance)
-                               if err := 
controllerutil.SetControllerReference(instance, authSecret, r.Scheme); err != 
nil {
-                                       return requeueOrNot, err
-                               }
-                               if err := 
controllerutil.SetControllerReference(instance, bootstrapSecret, r.Scheme); err 
!= nil {
-                                       return requeueOrNot, err
-                               }
-                               err = r.Create(ctx, authSecret)
-                               if err != nil {
-                                       return requeueOrNot, err
-                               }
-                               err = r.Create(ctx, bootstrapSecret)
-                               if err == nil {
-                                       // supply the bootstrap security.json 
to the initContainer via a simple BASE64 encoding env var
-                                       
reconcileConfigInfo[util.SecurityJsonFile] = 
string(bootstrapSecret.Data[util.SecurityJsonFile])
-                               }
-
-                               basicAuthSecret = authSecret
-                       }
-                       if err != nil {
-                               return requeueOrNot, err
-                       }
-
-                       if reconcileConfigInfo[util.SecurityJsonFile] == "" {
-                               // the bootstrap secret already exists, so just 
stash the security.json needed for constructing initContainers
-                               bootstrapSecret := &corev1.Secret{}
-                               err = r.Get(ctx, types.NamespacedName{Name: 
instance.SecurityBootstrapSecretName(), Namespace: instance.Namespace}, 
bootstrapSecret)
-                               if err != nil {
-                                       if !errors.IsNotFound(err) {
-                                               return requeueOrNot, err
-                                       } // else perhaps the user deleted it 
after security was bootstrapped ... this is ok but may trigger a restart on the 
STS
-                               } else {
-                                       // stash this so we can configure the 
setup-zk initContainer to bootstrap the security.json in ZK
-                                       
reconcileConfigInfo[util.SecurityJsonFile] = 
string(bootstrapSecret.Data[util.SecurityJsonFile])
-                               }
-                       }
+               security, err = util.ReconcileSecurityConfig(ctx, &r.Client, 
instance)
+               if err != nil {
+                       return requeueOrNot, err
                }
-
-               reconcileConfigInfo[corev1.BasicAuthUsernameKey] = 
string(basicAuthSecret.Data[corev1.BasicAuthUsernameKey])
-
-               // need the creds below for getting CLUSTERSTATUS
-               basicAuthHeader = util.BasicAuthHeader(basicAuthSecret)
        }
 
        // Only create stateful set if zkConnectionString can be found (must 
contain host and port)
@@ -387,7 +314,7 @@ func (r *SolrCloudReconciler) Reconcile(ctx 
context.Context, req ctrl.Request) (
 
        if !blockReconciliationOfStatefulSet {
                // Generate StatefulSet
-               statefulSet := util.GenerateStatefulSet(instance, &newStatus, 
hostNameIpMap, reconcileConfigInfo, tls)
+               statefulSet := util.GenerateStatefulSet(instance, &newStatus, 
hostNameIpMap, reconcileConfigInfo, tls, security)
 
                // Check if the StatefulSet already exists
                statefulSetLogger := logger.WithValues("statefulSet", 
statefulSet.Name)
@@ -481,10 +408,10 @@ func (r *SolrCloudReconciler) Reconcile(ctx 
context.Context, req ctrl.Request) (
                        logger.Info("Pod killed for update.", "pod", pod.Name, 
"reason", "The solr container in the pod has not yet started, thus it is safe 
to update.")
                }
 
-               // If authn enabled on Solr, we need to pass the basic auth 
header
+               // If authn enabled on Solr, we need to pass the auth header
                var authHeader map[string]string
-               if basicAuthHeader != "" {
-                       authHeader = map[string]string{"Authorization": 
basicAuthHeader}
+               if security != nil {
+                       authHeader = security.AuthHeader()
                }
 
                // Pick which pods should be deleted for an update.
diff --git a/controllers/solrcloud_controller_tls_test.go 
b/controllers/solrcloud_controller_tls_test.go
index 5c2b2bd..b5d1b2e 100644
--- a/controllers/solrcloud_controller_tls_test.go
+++ b/controllers/solrcloud_controller_tls_test.go
@@ -54,6 +54,7 @@ var _ = FDescribe("SolrCloud controller - TLS", func() {
                                Replicas: &replicas,
                                ZookeeperRef: &solrv1beta1.ZookeeperRef{
                                        ConnectionInfo: 
&solrv1beta1.ZookeeperConnectionInfo{
+                                               ChRoot:                   
"tls-test",
                                                InternalConnectionString: 
"host:7271",
                                        },
                                },
@@ -795,6 +796,7 @@ func expectZkSetupInitContainerForTLSWithGomega(g Gomega, 
solrCloud *solrv1beta1
                        break
                }
        }
+       expChrootCmd := "solr zk ls ${ZK_CHROOT} -z ${ZK_SERVER} || solr zk 
mkroot ${ZK_CHROOT} -z ${ZK_SERVER};"
        expCmd := "/opt/solr/server/scripts/cloud-scripts/zkcli.sh -zkhost 
${ZK_HOST} -cmd clusterprop -name urlScheme -val https"
        if solrCloud.Spec.SolrTLS != nil {
                g.Expect(zkSetupInitContainer).To(Not(BeNil()), "Didn't find 
the zk-setup InitContainer in the sts!")
@@ -802,6 +804,7 @@ func expectZkSetupInitContainerForTLSWithGomega(g Gomega, 
solrCloud *solrv1beta1
                        
g.Expect(zkSetupInitContainer.Image).To(Equal(statefulSet.Spec.Template.Spec.Containers[0].Image),
 "The zk-setup init container should use the same image as the Solr container")
                        g.Expect(zkSetupInitContainer.Command).To(HaveLen(3), 
"Wrong command length for zk-setup init container")
                        
g.Expect(zkSetupInitContainer.Command[2]).To(ContainSubstring(expCmd), "ZK 
Setup command does not set urlScheme")
+                       
g.Expect(zkSetupInitContainer.Command[2]).To(ContainSubstring(expChrootCmd), 
"ZK Setup command does init the chroot")
                        expNumVars := 3
                        if solrCloud.Spec.SolrSecurity != nil && 
solrCloud.Spec.SolrSecurity.BasicAuthSecret == "" {
                                expNumVars = 4 // one more for SECURITY_JSON
diff --git a/controllers/util/prometheus_exporter_util.go 
b/controllers/util/prometheus_exporter_util.go
index f5a122a..fb92a68 100644
--- a/controllers/util/prometheus_exporter_util.go
+++ b/controllers/util/prometheus_exporter_util.go
@@ -164,11 +164,7 @@ func 
GenerateSolrPrometheusExporterDeployment(solrPrometheusExporter *solr.SolrP
 
        // basic auth enabled?
        if solrPrometheusExporter.Spec.SolrReference.BasicAuthSecret != "" {
-               lor := corev1.LocalObjectReference{Name: 
solrPrometheusExporter.Spec.SolrReference.BasicAuthSecret}
-               usernameRef := &corev1.SecretKeySelector{LocalObjectReference: 
lor, Key: corev1.BasicAuthUsernameKey}
-               passwordRef := &corev1.SecretKeySelector{LocalObjectReference: 
lor, Key: corev1.BasicAuthPasswordKey}
-               envVars = append(envVars, corev1.EnvVar{Name: 
"BASIC_AUTH_USER", ValueFrom: &corev1.EnvVarSource{SecretKeyRef: usernameRef}})
-               envVars = append(envVars, corev1.EnvVar{Name: 
"BASIC_AUTH_PASS", ValueFrom: &corev1.EnvVarSource{SecretKeyRef: passwordRef}})
+               envVars = append(envVars, 
BasicAuthEnvVars(solrPrometheusExporter.Spec.SolrReference.BasicAuthSecret)...)
                allJavaOpts = append(allJavaOpts, 
"-Dbasicauth=$(BASIC_AUTH_USER):$(BASIC_AUTH_PASS)")
                allJavaOpts = append(allJavaOpts, 
"-Dsolr.httpclient.builder.factory=org.apache.solr.client.solrj.impl.PreemptiveBasicAuthClientBuilderFactory")
        }
diff --git a/controllers/util/solr_security_util.go 
b/controllers/util/solr_security_util.go
new file mode 100644
index 0000000..9701ab2
--- /dev/null
+++ b/controllers/util/solr_security_util.go
@@ -0,0 +1,433 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package util
+
+import (
+       "context"
+       "crypto/sha256"
+       b64 "encoding/base64"
+       "encoding/json"
+       "fmt"
+       solr "github.com/apache/solr-operator/api/v1beta1"
+       appsv1 "k8s.io/api/apps/v1"
+       corev1 "k8s.io/api/core/v1"
+       "k8s.io/apimachinery/pkg/api/errors"
+       metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+       "k8s.io/apimachinery/pkg/types"
+       "math/rand"
+       "regexp"
+       "sigs.k8s.io/controller-runtime/pkg/client"
+       "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
+       "strings"
+       "time"
+)
+
+const (
+       SecurityJsonFile       = "security.json"
+       BasicAuthMd5Annotation = "solr.apache.org/basicAuthMd5"
+       DefaultProbePath       = "/admin/info/system"
+)
+
+type SecurityConfig struct {
+       BasicAuthSecret *corev1.Secret
+       SecurityJson    string
+}
+
+// Given a SolrCloud instance and an API service client, produce a 
SecurityConfig needed to enable Solr security
+func ReconcileSecurityConfig(ctx context.Context, client *client.Client, 
instance *solr.SolrCloud) (*SecurityConfig, error) {
+       reader := *client
+
+       security := &SecurityConfig{}
+       basicAuthSecret := &corev1.Secret{}
+
+       // user has the option of providing a secret with credentials the 
operator should use to make requests to Solr
+       sec := instance.Spec.SolrSecurity
+
+       if sec.AuthenticationType != solr.Basic {
+               return nil, fmt.Errorf("%s not supported! Only 'Basic' 
authentication is supported by the Solr operator",
+                       instance.Spec.SolrSecurity.AuthenticationType)
+       }
+
+       // TODO: we shouldn't need to enforce this restriction?!?
+       //
+       // for now, we don't support 'solrSecurity.probesRequireAuth=true' and 
custom probe paths,
+       // so make the user fix that so there are no surprises later
+       if sec.ProbesRequireAuth && 
instance.Spec.CustomSolrKubeOptions.PodOptions != nil {
+               for _, path := range GetCustomProbePaths(instance) {
+                       if path != DefaultProbePath {
+                               return nil, fmt.Errorf(
+                                       "custom probe path %s not supported 
when 'solrSecurity.probesRequireAuth=true'; must use 
'solrSecurity.probesRequireAuth=false' when using custom probe endpoints", path)
+                       }
+               }
+       }
+
+       if sec.BasicAuthSecret != "" {
+               // the user supplied their own basic auth secret, make sure it 
exists and has the expected keys
+               if err := reader.Get(ctx, types.NamespacedName{Name: 
sec.BasicAuthSecret, Namespace: instance.Namespace}, basicAuthSecret); err != 
nil {
+                       return nil, err
+               }
+
+               err := ValidateBasicAuthSecret(basicAuthSecret)
+               if err != nil {
+                       return nil, err
+               }
+
+               // since the user supplied us with a basic auth secret, we're 
assuming they're also bootstrapping the security.json,
+               // so there is no bootstrap secret in this case
+
+       } else {
+               // We're supplying a secret with random passwords and a default 
security.json
+               // since we randomly generate the passwords, we need to lookup 
the secret first and only create if not exist
+               err := reader.Get(ctx, types.NamespacedName{Name: 
instance.BasicAuthSecretName(), Namespace: instance.Namespace}, basicAuthSecret)
+               if err != nil && errors.IsNotFound(err) {
+                       authSecret, bootstrapSecret := 
generateBasicAuthSecretWithBootstrap(instance)
+
+                       // take ownership of these secrets since we created them
+                       if err := 
controllerutil.SetControllerReference(instance, authSecret, reader.Scheme()); 
err != nil {
+                               return nil, err
+                       }
+                       if err := 
controllerutil.SetControllerReference(instance, bootstrapSecret, 
reader.Scheme()); err != nil {
+                               return nil, err
+                       }
+                       err = reader.Create(ctx, authSecret)
+                       if err != nil {
+                               return nil, err
+                       }
+                       err = reader.Create(ctx, bootstrapSecret)
+                       if err != nil {
+                               return nil, err
+                       }
+
+                       // supply the bootstrap security.json to the 
initContainer via a simple BASE64 encoding env var
+                       security.SecurityJson = 
string(bootstrapSecret.Data[SecurityJsonFile])
+                       basicAuthSecret = authSecret
+               }
+
+               if err != nil {
+                       return nil, err
+               }
+
+               security.BasicAuthSecret = basicAuthSecret
+
+               if security.SecurityJson == "" {
+                       // the bootstrap secret already exists, so just stash 
the security.json needed for constructing initContainers
+                       bootstrapSecret := &corev1.Secret{}
+                       err = reader.Get(ctx, types.NamespacedName{Name: 
instance.SecurityBootstrapSecretName(), Namespace: instance.Namespace}, 
bootstrapSecret)
+                       if err != nil {
+                               if !errors.IsNotFound(err) {
+                                       return nil, err
+                               } // else perhaps the user deleted it after 
security was bootstrapped ... this is ok but may trigger a restart on the STS
+                       } else {
+                               // stash this so we can configure the setup-zk 
initContainer to bootstrap the security.json in ZK
+                               security.SecurityJson = 
string(bootstrapSecret.Data[SecurityJsonFile])
+                       }
+               }
+       }
+
+       return security, nil
+}
+
+func enableSecureProbesOnSolrCloudStatefulSet(solrCloud *solr.SolrCloud, 
stateful *appsv1.StatefulSet) {
+       mainContainer := &stateful.Spec.Template.Spec.Containers[0]
+
+       // if probes require auth or Solr wants client auth (mTLS), need to 
invoke a command on the Solr pod for the probes
+       mountPath := ""
+       if solrCloud.Spec.SolrSecurity != nil && 
solrCloud.Spec.SolrSecurity.ProbesRequireAuth {
+               vol, volMount := 
secureProbeVolumeAndMount(solrCloud.BasicAuthSecretName())
+               if vol != nil {
+                       stateful.Spec.Template.Spec.Volumes = 
append(stateful.Spec.Template.Spec.Volumes, *vol)
+               }
+               if volMount != nil {
+                       mainContainer.VolumeMounts = 
append(mainContainer.VolumeMounts, *volMount)
+                       mountPath = volMount.MountPath
+               }
+       }
+
+       // update the probes if they are using HTTPGet to use an Exec to call 
Solr with TLS and/or Basic Auth creds
+       if mainContainer.LivenessProbe.HTTPGet != nil {
+               useSecureProbe(solrCloud, mainContainer.LivenessProbe, 
mountPath)
+       }
+       if mainContainer.ReadinessProbe.HTTPGet != nil {
+               useSecureProbe(solrCloud, mainContainer.ReadinessProbe, 
mountPath)
+       }
+       if mainContainer.StartupProbe != nil && 
mainContainer.StartupProbe.HTTPGet != nil {
+               useSecureProbe(solrCloud, mainContainer.StartupProbe, mountPath)
+       }
+}
+
+func cmdToPutSecurityJsonInZk() string {
+       scriptsDir := "/opt/solr/server/scripts/cloud-scripts"
+       cmd := " ZK_SECURITY_JSON=$(%s/zkcli.sh -zkhost ${ZK_HOST} -cmd get 
/security.json); "
+       cmd += "if [ ${#ZK_SECURITY_JSON} -lt 3 ]; then echo $SECURITY_JSON > 
/tmp/security.json; %s/zkcli.sh -zkhost ${ZK_HOST} -cmd putfile /security.json 
/tmp/security.json; echo \"put security.json in ZK\"; fi"
+       return fmt.Sprintf(cmd, scriptsDir, scriptsDir)
+}
+
+func (security *SecurityConfig) AuthHeader() map[string]string {
+       if security.BasicAuthSecret != nil {
+               return map[string]string{"Authorization": 
BasicAuthHeader(security.BasicAuthSecret)}
+       }
+       return nil
+}
+
+func BasicAuthEnvVars(secretName string) []corev1.EnvVar {
+       lor := corev1.LocalObjectReference{Name: secretName}
+       usernameRef := &corev1.SecretKeySelector{LocalObjectReference: lor, 
Key: corev1.BasicAuthUsernameKey}
+       passwordRef := &corev1.SecretKeySelector{LocalObjectReference: lor, 
Key: corev1.BasicAuthPasswordKey}
+       return []corev1.EnvVar{
+               {Name: "BASIC_AUTH_USER", ValueFrom: 
&corev1.EnvVarSource{SecretKeyRef: usernameRef}},
+               {Name: "BASIC_AUTH_PASS", ValueFrom: 
&corev1.EnvVarSource{SecretKeyRef: passwordRef}},
+       }
+}
+
+func BasicAuthHeader(basicAuthSecret *corev1.Secret) string {
+       creds := fmt.Sprintf("%s:%s", 
basicAuthSecret.Data[corev1.BasicAuthUsernameKey], 
basicAuthSecret.Data[corev1.BasicAuthPasswordKey])
+       return "Basic " + b64.StdEncoding.EncodeToString([]byte(creds))
+}
+
+func ValidateBasicAuthSecret(basicAuthSecret *corev1.Secret) error {
+       if basicAuthSecret.Type != corev1.SecretTypeBasicAuth {
+               return fmt.Errorf("invalid secret type %v; user-provided secret 
%s must be of type: %v",
+                       basicAuthSecret.Type, basicAuthSecret.Name, 
corev1.SecretTypeBasicAuth)
+       }
+
+       if _, ok := basicAuthSecret.Data[corev1.BasicAuthUsernameKey]; !ok {
+               return fmt.Errorf("%s key not found in user-provided basic-auth 
secret %s",
+                       corev1.BasicAuthUsernameKey, basicAuthSecret.Name)
+       }
+
+       if _, ok := basicAuthSecret.Data[corev1.BasicAuthPasswordKey]; !ok {
+               return fmt.Errorf("%s key not found in user-provided basic-auth 
secret %s",
+                       corev1.BasicAuthPasswordKey, basicAuthSecret.Name)
+       }
+
+       return nil
+}
+
+func generateBasicAuthSecretWithBootstrap(solrCloud *solr.SolrCloud) 
(*corev1.Secret, *corev1.Secret) {
+       securityBootstrapInfo := generateSecurityJson(solrCloud)
+
+       labels := solrCloud.SharedLabelsWith(solrCloud.GetLabels())
+       var annotations map[string]string
+       basicAuthSecret := &corev1.Secret{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:        solrCloud.BasicAuthSecretName(),
+                       Namespace:   solrCloud.GetNamespace(),
+                       Labels:      labels,
+                       Annotations: annotations,
+               },
+               Data: map[string][]byte{
+                       corev1.BasicAuthUsernameKey: 
[]byte(solr.DefaultBasicAuthUsername),
+                       corev1.BasicAuthPasswordKey: 
securityBootstrapInfo[solr.DefaultBasicAuthUsername],
+               },
+               Type: corev1.SecretTypeBasicAuth,
+       }
+
+       // this secret holds the admin and solr user credentials and the 
security.json needed to bootstrap Solr security
+       // once the security.json is created using the setup-zk initContainer, 
it is not updated by the operator
+       boostrapSecuritySecret := &corev1.Secret{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:        solrCloud.SecurityBootstrapSecretName(),
+                       Namespace:   solrCloud.GetNamespace(),
+                       Labels:      labels,
+                       Annotations: annotations,
+               },
+               Data: map[string][]byte{
+                       "admin":          securityBootstrapInfo["admin"],
+                       "solr":           securityBootstrapInfo["solr"],
+                       SecurityJsonFile: 
securityBootstrapInfo[SecurityJsonFile],
+               },
+               Type: corev1.SecretTypeOpaque,
+       }
+
+       return basicAuthSecret, boostrapSecuritySecret
+}
+
+func generateSecurityJson(solrCloud *solr.SolrCloud) map[string][]byte {
+       blockUnknown := true
+
+       probeRole := "\"k8s\"" // probe endpoints are secures
+       if !solrCloud.Spec.SolrSecurity.ProbesRequireAuth {
+               blockUnknown = false
+               probeRole = "null" // a JSON null value here to allow open 
access
+       }
+
+       probeAuthz := ""
+       for i, p := range getProbePaths(solrCloud) {
+               if i > 0 {
+                       probeAuthz += ", "
+               }
+               if strings.HasPrefix(p, "/solr") {
+                       p = p[len("/solr"):]
+               }
+               probeAuthz += fmt.Sprintf("{ \"name\": \"k8s-probe-%d\", 
\"role\":%s, \"collection\": null, \"path\":\"%s\" }", i, probeRole, p)
+       }
+
+       // Create the user accounts for security.json with random passwords
+       // hashed with random salt, just as Solr's hashing works
+       username := solr.DefaultBasicAuthUsername
+       users := []string{"admin", username, "solr"}
+       secretData := make(map[string][]byte, len(users))
+       credentials := make(map[string]string, len(users))
+       for _, u := range users {
+               secretData[u] = randomPassword()
+               credentials[u] = solrPasswordHash(secretData[u])
+       }
+       credentialsJson, _ := json.Marshal(credentials)
+
+       securityJson := fmt.Sprintf(`{
+      "authentication":{
+        "blockUnknown": %t,
+        "class":"solr.BasicAuthPlugin",
+        "credentials": %s,
+        "realm":"Solr Basic Auth",
+        "forwardCredentials": false
+      },
+      "authorization": {
+        "class": "solr.RuleBasedAuthorizationPlugin",
+        "user-role": {
+          "admin": ["admin", "k8s"],
+          "%s": ["k8s"],
+          "solr": ["users", "k8s"]
+        },
+        "permissions": [
+          %s,
+          { "name": "k8s-status", "role":"k8s", "collection": null, 
"path":"/admin/collections" },
+          { "name": "k8s-metrics", "role":"k8s", "collection": null, 
"path":"/admin/metrics" },
+          { "name": "k8s-zk", "role":"k8s", "collection": null, 
"path":"/admin/zookeeper/status" },
+          { "name": "k8s-ping", "role":"k8s", "collection": "*", 
"path":"/admin/ping" },
+          { "name": "read", "role":["admin","users"] },
+          { "name": "update", "role":["admin"] },
+          { "name": "security-read", "role": ["admin"] },
+          { "name": "security-edit", "role": ["admin"] },
+          { "name": "all", "role":["admin"] }
+        ]
+      }
+    }`, blockUnknown, credentialsJson, username, probeAuthz)
+
+       // we need to store the security.json in the secret, otherwise we'd 
recompute it for every reconcile loop
+       // but that doesn't work for randomized passwords ...
+       secretData[SecurityJsonFile] = []byte(securityJson)
+
+       return secretData
+}
+
+func randomPassword() []byte {
+       rand.Seed(time.Now().UnixNano())
+       lower := "abcdefghijklmnpqrstuvwxyz" // no 'o'
+       upper := strings.ToUpper(lower)
+       digits := "0123456789"
+       chars := lower + upper + digits + "()[]%#@-()[]%#@-"
+       pass := make([]byte, 16)
+       // start with a lower char and end with an upper
+       pass[0] = lower[rand.Intn(len(lower))]
+       pass[len(pass)-1] = upper[rand.Intn(len(upper))]
+       perm := rand.Perm(len(chars))
+       for i := 1; i < len(pass)-1; i++ {
+               pass[i] = chars[perm[i]]
+       }
+       return pass
+}
+
+func randomSaltHash() []byte {
+       b := make([]byte, 32)
+       rand.Read(b)
+       salt := sha256.Sum256(b)
+       return salt[:]
+}
+
+// this mimics the password hash generation approach used by Solr
+func solrPasswordHash(passBytes []byte) string {
+       // combine password with salt to create the hash
+       salt := randomSaltHash()
+       passHashBytes := sha256.Sum256(append(salt[:], passBytes...))
+       passHashBytes = sha256.Sum256(passHashBytes[:])
+       passHash := b64.StdEncoding.EncodeToString(passHashBytes[:])
+       return fmt.Sprintf("%s %s", passHash, 
b64.StdEncoding.EncodeToString(salt))
+}
+
+// Gets a list of probe paths we need to setup authz for
+func getProbePaths(solrCloud *solr.SolrCloud) []string {
+       probePaths := []string{DefaultProbePath}
+       probePaths = append(probePaths, GetCustomProbePaths(solrCloud)...)
+       return uniqueProbePaths(probePaths)
+}
+
+func uniqueProbePaths(paths []string) []string {
+       keys := make(map[string]bool)
+       var set []string
+       for _, name := range paths {
+               if _, exists := keys[name]; !exists {
+                       keys[name] = true
+                       set = append(set, name)
+               }
+       }
+       return set
+}
+
+func secureProbeVolumeAndMount(secretName string) (*corev1.Volume, 
*corev1.VolumeMount) {
+       vol := &corev1.Volume{
+               Name: strings.ReplaceAll(secretName, ".", "-"),
+               VolumeSource: corev1.VolumeSource{
+                       Secret: &corev1.SecretVolumeSource{
+                               SecretName:  secretName,
+                               DefaultMode: &SecretReadOnlyPermissions,
+                       },
+               },
+       }
+       volMount := &corev1.VolumeMount{Name: vol.Name, MountPath: 
fmt.Sprintf("/etc/secrets/%s", vol.Name)}
+       return vol, volMount
+}
+
+// When running with TLS and clientAuth=Need or if the probe endpoints require 
auth, we need to use a command instead of HTTP Get
+// This function builds the custom probe command and returns any associated 
volume / mounts needed for the auth secrets
+func useSecureProbe(solrCloud *solr.SolrCloud, probe *corev1.Probe, mountPath 
string) {
+       // mount the secret in a file so it gets updated; env vars do not see:
+       // 
https://kubernetes.io/docs/concepts/configuration/secret/#environment-variables-are-not-updated-after-a-secret-update
+       basicAuthOption := ""
+       enableBasicAuth := ""
+       if solrCloud.Spec.SolrSecurity != nil && 
solrCloud.Spec.SolrSecurity.ProbesRequireAuth {
+               usernameFile := fmt.Sprintf("%s/%s", mountPath, 
corev1.BasicAuthUsernameKey)
+               passwordFile := fmt.Sprintf("%s/%s", mountPath, 
corev1.BasicAuthPasswordKey)
+               basicAuthOption = fmt.Sprintf("-Dbasicauth=$(cat %s):$(cat 
%s)", usernameFile, passwordFile)
+               enableBasicAuth = " 
-Dsolr.httpclient.builder.factory=org.apache.solr.client.solrj.impl.PreemptiveBasicAuthClientBuilderFactory
 "
+       }
+
+       // Is TLS enabled? If so we need some additional SSL related props
+       tlsJavaToolOpts, tlsJavaSysProps := 
secureProbeTLSJavaToolOpts(solrCloud)
+       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 %s %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, tlsJavaSysProps, enableBasicAuth, 
solrCloud.UrlScheme(false), probe.HTTPGet.Port.IntVal, probe.HTTPGet.Path)
+       probeCommand = 
regexp.MustCompile(`\s+`).ReplaceAllString(strings.TrimSpace(probeCommand), " ")
+
+       // use an Exec instead of an HTTP GET
+       probe.HTTPGet = nil
+       probe.Exec = &corev1.ExecAction{Command: []string{"sh", "-c", 
probeCommand}}
+
+       // minimum of 5 seconds for exec probes as they are slow to initialize
+       if probe.TimeoutSeconds < 5 {
+               probe.TimeoutSeconds = 5
+       }
+}
diff --git a/controllers/util/solr_tls_util.go 
b/controllers/util/solr_tls_util.go
index a87993c..14d9251 100644
--- a/controllers/util/solr_tls_util.go
+++ b/controllers/util/solr_tls_util.go
@@ -722,8 +722,7 @@ func mountedTLSPath(dir *solr.MountedTLSDirectory, fileName 
string, defaultName
 
 // Command to set the urlScheme cluster prop to "https"
 func setUrlSchemeClusterPropCmd() string {
-       return "solr zk ls ${ZK_CHROOT} -z ${ZK_SERVER} || solr zk mkroot 
${ZK_CHROOT} -z ${ZK_SERVER}" +
-               "; /opt/solr/server/scripts/cloud-scripts/zkcli.sh -zkhost 
${ZK_HOST} -cmd clusterprop -name urlScheme -val https" +
+       return "/opt/solr/server/scripts/cloud-scripts/zkcli.sh -zkhost 
${ZK_HOST} -cmd clusterprop -name urlScheme -val https" +
                "; /opt/solr/server/scripts/cloud-scripts/zkcli.sh -zkhost 
${ZK_HOST} -cmd get /clusterprops.json;"
 }
 
diff --git a/controllers/util/solr_util.go b/controllers/util/solr_util.go
index b73670c..e2d9c8a 100644
--- a/controllers/util/solr_util.go
+++ b/controllers/util/solr_util.go
@@ -18,23 +18,16 @@
 package util
 
 import (
-       "crypto/sha256"
-       b64 "encoding/base64"
-       "encoding/json"
        "fmt"
-       "math/rand"
-       "regexp"
-       "sort"
-       "strconv"
-       "strings"
-       "time"
-
        solr "github.com/apache/solr-operator/api/v1beta1"
        appsv1 "k8s.io/api/apps/v1"
        corev1 "k8s.io/api/core/v1"
        netv1 "k8s.io/api/networking/v1"
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
        "k8s.io/apimachinery/pkg/util/intstr"
+       "sort"
+       "strconv"
+       "strings"
 )
 
 const (
@@ -56,9 +49,6 @@ const (
        SolrXmlFile                      = "solr.xml"
        LogXmlMd5Annotation              = "solr.apache.org/logXmlMd5"
        LogXmlFile                       = "log4j2.xml"
-       SecurityJsonFile                 = "security.json"
-       BasicAuthMd5Annotation           = "solr.apache.org/basicAuthMd5"
-       DefaultProbePath                 = "/admin/info/system"
 
        DefaultStatefulSetPodManagementPolicy = appsv1.ParallelPodManagement
 )
@@ -68,7 +58,7 @@ const (
 // replicas: the number of replicas for the SolrCloud instance
 // storage: the size of the storage for the SolrCloud instance (e.g. 100Gi)
 // zkConnectionString: the connectionString of the ZK instance to connect to
-func GenerateStatefulSet(solrCloud *solr.SolrCloud, solrCloudStatus 
*solr.SolrCloudStatus, hostNameIPs map[string]string, reconcileConfigInfo 
map[string]string, tls *TLSCerts) *appsv1.StatefulSet {
+func GenerateStatefulSet(solrCloud *solr.SolrCloud, solrCloudStatus 
*solr.SolrCloudStatus, hostNameIPs map[string]string, reconcileConfigInfo 
map[string]string, tls *TLSCerts, security *SecurityConfig) *appsv1.StatefulSet 
{
        terminationGracePeriod := int64(60)
        solrPodPort := solrCloud.Spec.SolrAddressability.PodPort
        fsGroup := int64(DefaultSolrGroup)
@@ -357,19 +347,6 @@ func GenerateStatefulSet(solrCloud *solr.SolrCloud, 
solrCloudStatus *solr.SolrCl
                }
        }
 
-       if (tls != nil && tls.ServerConfig != nil && 
tls.ServerConfig.Options.ClientAuth != solr.None) || 
(solrCloud.Spec.SolrSecurity != nil && 
solrCloud.Spec.SolrSecurity.ProbesRequireAuth) {
-               probeCommand, vol, volMount := 
configureSecureProbeCommand(solrCloud, defaultHandler.HTTPGet)
-               if vol != nil {
-                       solrVolumes = append(solrVolumes, *vol)
-               }
-               if volMount != nil {
-                       volumeMounts = append(volumeMounts, *volMount)
-               }
-               // 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,
        // so we get a rolling restart when the configMap changes
        if reconcileConfigInfo[SolrXmlMd5Annotation] != "" {
@@ -389,7 +366,7 @@ func GenerateStatefulSet(solrCloud *solr.SolrCloud, 
solrCloudStatus *solr.SolrCl
                Value: strings.Join(allSolrOpts, " "),
        })
 
-       initContainers := generateSolrSetupInitContainers(solrCloud, 
solrCloudStatus, solrDataVolumeName, reconcileConfigInfo)
+       initContainers := generateSolrSetupInitContainers(solrCloud, 
solrCloudStatus, solrDataVolumeName, security)
 
        // Add user defined additional init containers
        if customPodOptions != nil && len(customPodOptions.InitContainers) > 0 {
@@ -563,10 +540,15 @@ func GenerateStatefulSet(solrCloud *solr.SolrCloud, 
solrCloudStatus *solr.SolrCl
                tls.enableTLSOnSolrCloudStatefulSet(stateful)
        }
 
+       // If probes require auth is set OR tls is configured to want / need 
client auth, then reconfigure the probes to use an exec
+       if (solrCloud.Spec.SolrSecurity != nil && 
solrCloud.Spec.SolrSecurity.ProbesRequireAuth) || (tls != nil && 
tls.ServerConfig != nil && tls.ServerConfig.Options.ClientAuth != solr.None) {
+               enableSecureProbesOnSolrCloudStatefulSet(solrCloud, stateful)
+       }
+
        return stateful
 }
 
-func generateSolrSetupInitContainers(solrCloud *solr.SolrCloud, 
solrCloudStatus *solr.SolrCloudStatus, solrDataVolumeName string, 
reconcileConfigInfo map[string]string) (containers []corev1.Container) {
+func generateSolrSetupInitContainers(solrCloud *solr.SolrCloud, 
solrCloudStatus *solr.SolrCloudStatus, solrDataVolumeName string, security 
*SecurityConfig) (containers []corev1.Container) {
        // The setup of the solr.xml will always be necessary
        volumeMounts := []corev1.VolumeMount{
                {
@@ -605,7 +587,7 @@ func generateSolrSetupInitContainers(solrCloud 
*solr.SolrCloud, solrCloudStatus
 
        containers = append(containers, volumePrepInitContainer)
 
-       if hasZKSetupContainer, zkSetupContainer := 
generateZKInteractionInitContainer(solrCloud, solrCloudStatus, 
reconcileConfigInfo); hasZKSetupContainer {
+       if hasZKSetupContainer, zkSetupContainer := 
generateZKInteractionInitContainer(solrCloud, solrCloudStatus, security); 
hasZKSetupContainer {
                containers = append(containers, zkSetupContainer)
        }
 
@@ -987,11 +969,11 @@ func CreateNodeIngressRule(solrCloud *solr.SolrCloud, 
nodeName string, domainNam
 }
 
 // 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) {
+func generateZKInteractionInitContainer(solrCloud *solr.SolrCloud, 
solrCloudStatus *solr.SolrCloudStatus, security *SecurityConfig) (bool, 
corev1.Container) {
        allSolrOpts := make([]string, 0)
 
        // Add all necessary ZK Info
-       envVars, zkSolrOpt, _ := createZkConnectionEnvVars(solrCloud, 
solrCloudStatus)
+       envVars, zkSolrOpt, hasChroot := createZkConnectionEnvVars(solrCloud, 
solrCloudStatus)
        if zkSolrOpt != "" {
                allSolrOpts = append(allSolrOpts, zkSolrOpt)
        }
@@ -1010,21 +992,21 @@ func generateZKInteractionInitContainer(solrCloud 
*solr.SolrCloud, solrCloudStat
 
        cmd := ""
 
+       if hasChroot {
+               cmd += "solr zk ls ${ZK_CHROOT} -z ${ZK_SERVER} || solr zk 
mkroot ${ZK_CHROOT} -z ${ZK_SERVER}; "
+       }
+
        if solrCloud.Spec.SolrTLS != nil {
-               cmd = setUrlSchemeClusterPropCmd()
+               cmd += setUrlSchemeClusterPropCmd()
        }
 
-       if reconcileConfigInfo[SecurityJsonFile] != "" {
+       if security != nil && security.SecurityJson != "" {
                envVars = append(envVars, corev1.EnvVar{Name: "SECURITY_JSON", 
ValueFrom: &corev1.EnvVarSource{
                        SecretKeyRef: &corev1.SecretKeySelector{
                                LocalObjectReference: 
corev1.LocalObjectReference{Name: solrCloud.SecurityBootstrapSecretName()},
                                Key:                  SecurityJsonFile}}})
 
-               if cmd == "" {
-                       cmd += "solr zk ls ${ZK_CHROOT} -z ${ZK_SERVER} || solr 
zk mkroot ${ZK_CHROOT} -z ${ZK_SERVER}; "
-               }
-               cmd += 
"ZK_SECURITY_JSON=$(/opt/solr/server/scripts/cloud-scripts/zkcli.sh -zkhost 
${ZK_HOST} -cmd get /security.json); "
-               cmd += "if [ ${#ZK_SECURITY_JSON} -lt 3 ]; then echo 
$SECURITY_JSON > /tmp/security.json; 
/opt/solr/server/scripts/cloud-scripts/zkcli.sh -zkhost ${ZK_HOST} -cmd putfile 
/security.json /tmp/security.json; echo \"put security.json in ZK\"; fi"
+               cmd += cmdToPutSecurityJsonInZk()
        }
 
        if cmd != "" {
@@ -1105,140 +1087,6 @@ func 
setupVolumeMountForUserProvidedConfigMapEntry(reconcileConfigInfo map[strin
        return &corev1.VolumeMount{Name: volName, MountPath: mountPath}, 
&corev1.EnvVar{Name: envVar, Value: pathToFile}, vol
 }
 
-func BasicAuthHeader(basicAuthSecret *corev1.Secret) string {
-       creds := fmt.Sprintf("%s:%s", 
basicAuthSecret.Data[corev1.BasicAuthUsernameKey], 
basicAuthSecret.Data[corev1.BasicAuthPasswordKey])
-       return "Basic " + b64.StdEncoding.EncodeToString([]byte(creds))
-}
-
-func ValidateBasicAuthSecret(basicAuthSecret *corev1.Secret) error {
-       if basicAuthSecret.Type != corev1.SecretTypeBasicAuth {
-               return fmt.Errorf("invalid secret type %v; user-provided secret 
%s must be of type: %v",
-                       basicAuthSecret.Type, basicAuthSecret.Name, 
corev1.SecretTypeBasicAuth)
-       }
-
-       if _, ok := basicAuthSecret.Data[corev1.BasicAuthUsernameKey]; !ok {
-               return fmt.Errorf("%s key not found in user-provided basic-auth 
secret %s",
-                       corev1.BasicAuthUsernameKey, basicAuthSecret.Name)
-       }
-
-       if _, ok := basicAuthSecret.Data[corev1.BasicAuthPasswordKey]; !ok {
-               return fmt.Errorf("%s key not found in user-provided basic-auth 
secret %s",
-                       corev1.BasicAuthPasswordKey, basicAuthSecret.Name)
-       }
-
-       return nil
-}
-
-func GenerateBasicAuthSecretWithBootstrap(solrCloud *solr.SolrCloud) 
(*corev1.Secret, *corev1.Secret) {
-
-       securityBootstrapInfo := generateSecurityJson(solrCloud)
-
-       labels := solrCloud.SharedLabelsWith(solrCloud.GetLabels())
-       var annotations map[string]string
-       basicAuthSecret := &corev1.Secret{
-               ObjectMeta: metav1.ObjectMeta{
-                       Name:        solrCloud.BasicAuthSecretName(),
-                       Namespace:   solrCloud.GetNamespace(),
-                       Labels:      labels,
-                       Annotations: annotations,
-               },
-               Data: map[string][]byte{
-                       corev1.BasicAuthUsernameKey: 
[]byte(solr.DefaultBasicAuthUsername),
-                       corev1.BasicAuthPasswordKey: 
securityBootstrapInfo[solr.DefaultBasicAuthUsername],
-               },
-               Type: corev1.SecretTypeBasicAuth,
-       }
-
-       // this secret holds the admin and solr user credentials and the 
security.json needed to bootstrap Solr security
-       // once the security.json is created using the setup-zk initContainer, 
it is not updated by the operator
-       boostrapSecuritySecret := &corev1.Secret{
-               ObjectMeta: metav1.ObjectMeta{
-                       Name:        solrCloud.SecurityBootstrapSecretName(),
-                       Namespace:   solrCloud.GetNamespace(),
-                       Labels:      labels,
-                       Annotations: annotations,
-               },
-               Data: map[string][]byte{
-                       "admin":          securityBootstrapInfo["admin"],
-                       "solr":           securityBootstrapInfo["solr"],
-                       SecurityJsonFile: 
securityBootstrapInfo[SecurityJsonFile],
-               },
-               Type: corev1.SecretTypeOpaque,
-       }
-
-       return basicAuthSecret, boostrapSecuritySecret
-}
-
-func generateSecurityJson(solrCloud *solr.SolrCloud) map[string][]byte {
-       blockUnknown := true
-
-       probeRole := "\"k8s\"" // probe endpoints are secures
-       if !solrCloud.Spec.SolrSecurity.ProbesRequireAuth {
-               blockUnknown = false
-               probeRole = "null" // a JSON null value here to allow open 
access
-       }
-
-       probePaths := getProbePaths(solrCloud)
-       probeAuthz := ""
-       for i, p := range probePaths {
-               if i > 0 {
-                       probeAuthz += ", "
-               }
-               if strings.HasPrefix(p, "/solr") {
-                       p = p[len("/solr"):]
-               }
-               probeAuthz += fmt.Sprintf("{ \"name\": \"k8s-probe-%d\", 
\"role\":%s, \"collection\": null, \"path\":\"%s\" }", i, probeRole, p)
-       }
-
-       // Create the user accounts for security.json with random passwords
-       // hashed with random salt, just as Solr's hashing works
-       username := solr.DefaultBasicAuthUsername
-       users := []string{"admin", username, "solr"}
-       secretData := make(map[string][]byte, len(users))
-       credentials := make(map[string]string, len(users))
-       for _, u := range users {
-               secretData[u] = randomPassword()
-               credentials[u] = solrPasswordHash(secretData[u])
-       }
-       credentialsJson, _ := json.Marshal(credentials)
-
-       securityJson := fmt.Sprintf(`{
-      "authentication":{
-        "blockUnknown": %t,
-        "class":"solr.BasicAuthPlugin",
-        "credentials": %s,
-        "realm":"Solr Basic Auth",
-        "forwardCredentials": false
-      },
-      "authorization": {
-        "class": "solr.RuleBasedAuthorizationPlugin",
-        "user-role": {
-          "admin": ["admin", "k8s"],
-          "%s": ["k8s"],
-          "solr": ["users", "k8s"]
-        },
-        "permissions": [
-          %s,
-          { "name": "k8s-status", "role":"k8s", "collection": null, 
"path":"/admin/collections" },
-          { "name": "k8s-metrics", "role":"k8s", "collection": null, 
"path":"/admin/metrics" },
-          { "name": "k8s-zk", "role":"k8s", "collection": null, 
"path":"/admin/zookeeper/status" },
-          { "name": "k8s-ping", "role":"k8s", "collection": "*", 
"path":"/admin/ping" },
-          { "name": "read", "role":["admin","users"] },
-          { "name": "update", "role":["admin"] },
-          { "name": "security-read", "role": ["admin"] },
-          { "name": "security-edit", "role": ["admin"] },
-          { "name": "all", "role":["admin"] }
-        ]
-      }
-    }`, blockUnknown, credentialsJson, username, probeAuthz)
-
-       // we need to store the security.json in the secret, otherwise we'd 
recompute it for every reconcile loop
-       // but that doesn't work for randomized passwords ...
-       secretData[SecurityJsonFile] = []byte(securityJson)
-
-       return secretData
-}
-
 func GetCustomProbePaths(solrCloud *solr.SolrCloud) []string {
        probePaths := []string{}
 
@@ -1262,102 +1110,3 @@ func GetCustomProbePaths(solrCloud *solr.SolrCloud) 
[]string {
 
        return probePaths
 }
-
-// Gets a list of probe paths we need to setup authz for
-func getProbePaths(solrCloud *solr.SolrCloud) []string {
-       probePaths := []string{DefaultProbePath}
-       probePaths = append(probePaths, GetCustomProbePaths(solrCloud)...)
-       return uniqueProbePaths(probePaths)
-}
-
-func randomPassword() []byte {
-       rand.Seed(time.Now().UnixNano())
-       lower := "abcdefghijklmnpqrstuvwxyz" // no 'o'
-       upper := strings.ToUpper(lower)
-       digits := "0123456789"
-       chars := lower + upper + digits + "()[]%#@-()[]%#@-"
-       pass := make([]byte, 16)
-       // start with a lower char and end with an upper
-       pass[0] = lower[rand.Intn(len(lower))]
-       pass[len(pass)-1] = upper[rand.Intn(len(upper))]
-       perm := rand.Perm(len(chars))
-       for i := 1; i < len(pass)-1; i++ {
-               pass[i] = chars[perm[i]]
-       }
-       return pass
-}
-
-func randomSaltHash() []byte {
-       b := make([]byte, 32)
-       rand.Read(b)
-       salt := sha256.Sum256(b)
-       return salt[:]
-}
-
-// this mimics the password hash generation approach used by Solr
-func solrPasswordHash(passBytes []byte) string {
-       // combine password with salt to create the hash
-       salt := randomSaltHash()
-       passHashBytes := sha256.Sum256(append(salt[:], passBytes...))
-       passHashBytes = sha256.Sum256(passHashBytes[:])
-       passHash := b64.StdEncoding.EncodeToString(passHashBytes[:])
-       return fmt.Sprintf("%s %s", passHash, 
b64.StdEncoding.EncodeToString(salt))
-}
-
-func uniqueProbePaths(paths []string) []string {
-       keys := make(map[string]bool)
-       var set []string
-       for _, name := range paths {
-               if _, exists := keys[name]; !exists {
-                       keys[name] = true
-                       set = append(set, name)
-               }
-       }
-       return set
-}
-
-// When running with TLS and clientAuth=Need or if the probe endpoints require 
auth, we need to use a command instead of HTTP Get
-// This function builds the custom probe command and returns any associated 
volume / mounts needed for the auth secrets
-func configureSecureProbeCommand(solrCloud *solr.SolrCloud, 
defaultProbeGetAction *corev1.HTTPGetAction) (string, *corev1.Volume, 
*corev1.VolumeMount) {
-       // mount the secret in a file so it gets updated; env vars do not see:
-       // 
https://kubernetes.io/docs/concepts/configuration/secret/#environment-variables-are-not-updated-after-a-secret-update
-       basicAuthOption := ""
-       enableBasicAuth := ""
-       var volMount *corev1.VolumeMount
-       var vol *corev1.Volume
-       if solrCloud.Spec.SolrSecurity != nil && 
solrCloud.Spec.SolrSecurity.ProbesRequireAuth {
-               secretName := solrCloud.BasicAuthSecretName()
-               vol = &corev1.Volume{
-                       Name: strings.ReplaceAll(secretName, ".", "-"),
-                       VolumeSource: corev1.VolumeSource{
-                               Secret: &corev1.SecretVolumeSource{
-                                       SecretName:  secretName,
-                                       DefaultMode: &SecretReadOnlyPermissions,
-                               },
-                       },
-               }
-               mountPath := fmt.Sprintf("/etc/secrets/%s", vol.Name)
-               volMount = &corev1.VolumeMount{Name: vol.Name, MountPath: 
mountPath}
-               usernameFile := fmt.Sprintf("%s/%s", mountPath, 
corev1.BasicAuthUsernameKey)
-               passwordFile := fmt.Sprintf("%s/%s", mountPath, 
corev1.BasicAuthPasswordKey)
-               basicAuthOption = fmt.Sprintf("-Dbasicauth=$(cat %s):$(cat 
%s)", usernameFile, passwordFile)
-               enableBasicAuth = " 
-Dsolr.httpclient.builder.factory=org.apache.solr.client.solrj.impl.PreemptiveBasicAuthClientBuilderFactory
 "
-       }
-
-       // Is TLS enabled? If so we need some additional SSL related props
-       tlsJavaToolOpts, tlsJavaSysProps := 
secureProbeTLSJavaToolOpts(solrCloud)
-       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 %s %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, tlsJavaSysProps, enableBasicAuth, 
solrCloud.UrlScheme(false), defaultProbeGetAction.Port.IntVal, 
defaultProbeGetAction.Path)
-       probeCommand = 
regexp.MustCompile(`\s+`).ReplaceAllString(strings.TrimSpace(probeCommand), " ")
-
-       return probeCommand, vol, volMount
-}

Reply via email to