From b9b97de13d3baa7e0cd2df6dabd5c247d543c31e Mon Sep 17 00:00:00 2001 From: Vivek Reddy Date: Mon, 21 Oct 2024 17:01:58 -0700 Subject: [PATCH] working c3 changes --- .env | 2 +- pkg/splunk/client/vault_setup.go | 69 +++++++++++++++++ pkg/splunk/enterprise/clustermanager.go | 9 +++ pkg/splunk/enterprise/clustermaster.go | 9 +++ pkg/splunk/enterprise/indexercluster.go | 86 +++++++++++++++++----- pkg/splunk/enterprise/licensemanager.go | 12 ++- pkg/splunk/enterprise/monitoringconsole.go | 11 ++- pkg/splunk/enterprise/searchheadcluster.go | 46 ++++++++++-- 8 files changed, 217 insertions(+), 27 deletions(-) diff --git a/.env b/.env index 7c166b57a..54c05905b 100644 --- a/.env +++ b/.env @@ -5,5 +5,5 @@ AWSCLI_URL=https://awscli.amazonaws.com/awscli-exe-linux-x86_64-2.8.6.zip KUBECTL_VERSION=v1.29.1 AZ_CLI_VERSION=2.30.0 EKSCTL_VERSION=v0.143.0 -EKS_CLUSTER_K8_VERSION=1.27 +EKS_CLUSTER_K8_VERSION=1.31 SPLUNK_ENTERPRISE_RELEASE_IMAGE=splunk/splunk:9.3.0 diff --git a/pkg/splunk/client/vault_setup.go b/pkg/splunk/client/vault_setup.go index 9d4a6c561..927b711bd 100644 --- a/pkg/splunk/client/vault_setup.go +++ b/pkg/splunk/client/vault_setup.go @@ -281,3 +281,72 @@ func CheckAndRestartStatefulSet(ctx context.Context, kubeClient splcommon.Contro return nil } + + +// GetSpecificSecretTokenFromVault retrieves a specific secret token's value from a Pod +func GetSpecificSecretTokenFromVault(ctx context.Context, c splcommon.ControllerClient, vaultIntegration *enterpriseApi.VaultIntegration, secretToken string) (string, error) { + logger.Info("CheckAndRestartStatefulSet called") + + // Initialize Vault client + client := resty.New() + client.SetDebug(true) //FIXME TODO remove once code complete + + // Read the Kubernetes service account token + tokenFile := "/var/run/secrets/kubernetes.io/serviceaccount/token" + token, err := os.ReadFile(tokenFile) + if err != nil { + logger.Error(err, "Failed to read service account token") + return "", fmt.Errorf("failed to read service account token: %v", err) + } + + // Authenticate with Vault using the Kubernetes auth method + data := map[string]interface{}{ + "role": vaultIntegration.Role, + "jwt": string(token), + } + var authResponse map[string]interface{} + resp, err := client.R(). + SetBody(data). + SetResult(&authResponse). + Post(fmt.Sprintf("%s/v1/auth/kubernetes/login", vaultIntegration.Address)) + if err != nil { + logger.Error(err, "Failed to authenticate with Vault") + return "", fmt.Errorf("failed to authenticate with Vault: %v", err) + } + if resp.StatusCode() != 200 { + logger.Error(fmt.Errorf("failed to authenticate with Vault"), "Vault authentication failed", "response", resp.String()) + return "", fmt.Errorf("failed to authenticate with Vault: %v", resp.String()) + } + + // Set the client token after successful authentication + tokenValue := authResponse["auth"].(map[string]interface{})["client_token"].(string) + logger.Info("Authenticated with Vault", "client_token", tokenValue) + + key := secretToken + // Construct the metadata path for each key + metadataPath := fmt.Sprintf("%s/%s", vaultIntegration.SecretPath, key) + if vaultIntegration.SecretPath[len(vaultIntegration.SecretPath)-1] == '/' { + metadataPath = fmt.Sprintf("%smetadata/%s", vaultIntegration.SecretPath, key) + } + vaultError := &VaultError{} + // Read the secret metadata from Vault to get the version + var metadataResponse VaultResponse + resp, err = client.R(). + SetHeader("X-Vault-Token", tokenValue). + SetResult(&metadataResponse). + SetError(vaultError). + ForceContentType("application/json"). + Get(fmt.Sprintf("%s/v1/%s", vaultIntegration.Address, metadataPath)) + if err != nil { + logger.Error(err, "Failed to read secret metadata from Vault", "metadataPath", metadataPath) + return "", fmt.Errorf("failed to read secret metadata from Vault: %v", err) + } + if resp.StatusCode() != 200 { + logger.Error(fmt.Errorf("failed to read secret metadata from Vault"), "Vault metadata read failed", "response", vaultError) + return "", fmt.Errorf("failed to read secret metadata from Vault: %v", vaultError) + } + + password := metadataResponse.Data.Data.Value + + return password, nil +} diff --git a/pkg/splunk/enterprise/clustermanager.go b/pkg/splunk/enterprise/clustermanager.go index 4ba7ca38a..7b0ca9d25 100644 --- a/pkg/splunk/enterprise/clustermanager.go +++ b/pkg/splunk/enterprise/clustermanager.go @@ -191,6 +191,15 @@ func ApplyClusterManager(ctx context.Context, client splcommon.ControllerClient, return result, err } + if cr.Spec.VaultIntegration.Enable { + //The InjectVaultSecret function is responsible for injecting secrets from HashiCorp Vault into the specified pod template. + splclient.InjectVaultSecret(ctx, client, statefulSet, &cr.Spec.VaultIntegration) + err := splclient.CheckAndRestartStatefulSet(ctx, client, statefulSet, &cr.Spec.VaultIntegration) + if err != nil { + return result, err + } + } + clusterManagerManager := splctrl.DefaultStatefulSetPodManager{} phase, err := clusterManagerManager.Update(ctx, client, statefulSet, 1) if err != nil { diff --git a/pkg/splunk/enterprise/clustermaster.go b/pkg/splunk/enterprise/clustermaster.go index f8a4cdb12..c646aad2b 100644 --- a/pkg/splunk/enterprise/clustermaster.go +++ b/pkg/splunk/enterprise/clustermaster.go @@ -183,6 +183,15 @@ func ApplyClusterMaster(ctx context.Context, client splcommon.ControllerClient, return result, err } + if cr.Spec.VaultIntegration.Enable { + //The InjectVaultSecret function is responsible for injecting secrets from HashiCorp Vault into the specified pod template. + splclient.InjectVaultSecret(ctx, client, statefulSet, &cr.Spec.VaultIntegration) + err := splclient.CheckAndRestartStatefulSet(ctx, client, statefulSet, &cr.Spec.VaultIntegration) + if err != nil { + return result, err + } + } + clusterMasterManager := splctrl.DefaultStatefulSetPodManager{} phase, err := clusterMasterManager.Update(ctx, client, statefulSet, 1) if err != nil { diff --git a/pkg/splunk/enterprise/indexercluster.go b/pkg/splunk/enterprise/indexercluster.go index 723e3c1a6..d60ae5c22 100644 --- a/pkg/splunk/enterprise/indexercluster.go +++ b/pkg/splunk/enterprise/indexercluster.go @@ -115,6 +115,7 @@ func ApplyIndexerClusterManager(ctx context.Context, client splcommon.Controller } mgr := newIndexerClusterPodManager(scopedLog, cr, namespaceScopedSecret, splclient.NewSplunkClient) + // Check if we have configured enough number(<= RF) of replicas if mgr.cr.Status.ClusterManagerPhase == enterpriseApi.PhaseReady { err = VerifyRFPeers(ctx, mgr, client) @@ -160,6 +161,15 @@ func ApplyIndexerClusterManager(ctx context.Context, client splcommon.Controller return result, err } + if cr.Spec.VaultIntegration.Enable { + //The InjectVaultSecret function is responsible for injecting secrets from HashiCorp Vault into the specified pod template. + splclient.InjectVaultSecret(ctx, client, statefulSet, &cr.Spec.VaultIntegration) + err := splclient.CheckAndRestartStatefulSet(ctx, client, statefulSet, &cr.Spec.VaultIntegration) + if err != nil { + return result, err + } + } + // Note: // This is a temporary fix for CSPL-1880. Splunk enterprise 9.0.0 fails when we migrate from 8.2.6. // Splunk 9.0.0 bundle push uses encryption while transferring data. If any of the @@ -412,6 +422,15 @@ func ApplyIndexerCluster(ctx context.Context, client splcommon.ControllerClient, return result, err } + if cr.Spec.VaultIntegration.Enable { + //The InjectVaultSecret function is responsible for injecting secrets from HashiCorp Vault into the specified pod template. + splclient.InjectVaultSecret(ctx, client, statefulSet, &cr.Spec.VaultIntegration) + err := splclient.CheckAndRestartStatefulSet(ctx, client, statefulSet, &cr.Spec.VaultIntegration) + if err != nil { + return result, err + } + } + // Note: // This is a fix for CSPL-1880. Splunk enterprise 9.0.0 fails when we migrate from 8.2.6. // Splunk 9.0.0 bundle push uses encryption while transferring data. If any of the @@ -564,6 +583,7 @@ type indexerClusterPodManager struct { cr *enterpriseApi.IndexerCluster secrets *corev1.Secret newSplunkClient func(managementURI, username, password string) *splclient.SplunkClient + vaultIntegration *enterpriseApi.VaultIntegration } // newIndexerClusterPodManager function to create pod manager this is added to write unit test case @@ -573,6 +593,7 @@ var newIndexerClusterPodManager = func(log logr.Logger, cr *enterpriseApi.Indexe cr: cr, secrets: secret, newSplunkClient: newSplunkClient, + vaultIntegration: &cr.Spec.VaultIntegration, } } @@ -584,10 +605,20 @@ func (mgr *indexerClusterPodManager) getMonitoringConsoleClient(cr *enterpriseAp // SetClusterMaintenanceMode enables/disables cluster maintenance mode func SetClusterMaintenanceMode(ctx context.Context, c splcommon.ControllerClient, cr *enterpriseApi.IndexerCluster, enable bool, cmPodName string, podExecClient splutil.PodExecClientImpl) error { - // Retrieve admin password from Pod - adminPwd, err := splutil.GetSpecificSecretTokenFromPod(ctx, c, cmPodName, cr.GetNamespace(), "password") - if err != nil { - return err + + var adminPwd string + var err error + if cr.Spec.VaultIntegration.Enable { + adminPwd, err = splclient.GetSpecificSecretTokenFromVault(ctx, c, &cr.Spec.VaultIntegration, "password") + if err != nil { + return err + } + } else { + // Retrieve admin password from Pod + adminPwd, err = splutil.GetSpecificSecretTokenFromPod(ctx, c, cmPodName, cr.GetNamespace(), "password") + if err != nil { + return err + } } var command string @@ -782,10 +813,13 @@ func (mgr *indexerClusterPodManager) Update(ctx context.Context, c splcommon.Con // Get the podExecClient with empty targetPodName. // This will be set inside ApplyIdxcSecret podExecClient := splutil.GetPodExecClient(mgr.c, mgr.cr, "") - // Check if a recycle of idxc pods is necessary(due to idxc_secret mismatch with CM) - err = ApplyIdxcSecret(ctx, mgr, desiredReplicas, podExecClient) - if err != nil { - return enterpriseApi.PhaseError, err + + if !mgr.cr.Spec.VaultIntegration.Enable { + // Check if a recycle of idxc pods is necessary(due to idxc_secret mismatch with CM) + err = ApplyIdxcSecret(ctx, mgr, desiredReplicas, podExecClient) + if err != nil { + return enterpriseApi.PhaseError, err + } } // update CR status with IDXC information @@ -884,16 +918,25 @@ func (mgr *indexerClusterPodManager) getClient(ctx context.Context, n int32) *sp fqdnName := splcommon.GetServiceFQDN(mgr.cr.GetNamespace(), fmt.Sprintf("%s.%s", memberName, GetSplunkServiceName(SplunkIndexer, mgr.cr.GetName(), true))) + var adminPwd string + var err error + if (mgr.vaultIntegration != nil && mgr.vaultIntegration.Enable) { + adminPwd, err = splclient.GetSpecificSecretTokenFromVault(ctx, mgr.c, mgr.vaultIntegration, "password") + if err != nil { + scopedLog.Error(err, "Couldn't retrieve the admin password from vault") + } + } else { // Retrieve admin password from Pod - adminPwd, err := splutil.GetSpecificSecretTokenFromPod(ctx, mgr.c, memberName, mgr.cr.GetNamespace(), "password") - if err != nil { - scopedLog.Error(err, "Couldn't retrieve the admin password from pod") + adminPwd, err = splutil.GetSpecificSecretTokenFromPod(ctx, mgr.c, memberName, mgr.cr.GetNamespace(), "password") + if err != nil { + scopedLog.Error(err, "Couldn't retrieve the admin password from pod") + } } return mgr.newSplunkClient(fmt.Sprintf("https://%s:8089", fqdnName), "admin", adminPwd) } -// getClusterManagerClient for indexerClusterPodManager returns a SplunkClient for cluster manager +// getClusterManagerClient for indexerClusterPodManager returns a SplunkClient for cluster manager. func (mgr *indexerClusterPodManager) getClusterManagerClient(ctx context.Context) *splclient.SplunkClient { reqLogger := log.FromContext(ctx) scopedLog := reqLogger.WithName("indexerClusterPodManager.getClusterManagerClient") @@ -914,11 +957,20 @@ func (mgr *indexerClusterPodManager) getClusterManagerClient(ctx context.Context // Get Fully Qualified Domain Name fqdnName := splcommon.GetServiceFQDN(mgr.cr.GetNamespace(), GetSplunkServiceName(cm, managerIdxcName, false)) - // Retrieve admin password for Pod - podName := fmt.Sprintf("splunk-%s-%s-%s", managerIdxcName, cm, "0") - adminPwd, err := splutil.GetSpecificSecretTokenFromPod(ctx, mgr.c, podName, mgr.cr.GetNamespace(), "password") - if err != nil { - scopedLog.Error(err, "Couldn't retrieve the admin password from pod") + var adminPwd string + var err error + if (mgr.vaultIntegration != nil && mgr.vaultIntegration.Enable) { + adminPwd, err = splclient.GetSpecificSecretTokenFromVault(ctx, mgr.c, mgr.vaultIntegration, "password") + if err != nil { + scopedLog.Error(err, "Couldn't retrieve the admin password from vault") + } + } else { + // Retrieve admin password for Pod + podName := fmt.Sprintf("splunk-%s-%s-%s", managerIdxcName, cm, "0") + adminPwd, err = splutil.GetSpecificSecretTokenFromPod(ctx, mgr.c, podName, mgr.cr.GetNamespace(), "password") + if err != nil { + scopedLog.Error(err, "Couldn't retrieve the admin password from pod") + } } return mgr.newSplunkClient(fmt.Sprintf("https://%s:8089", fqdnName), "admin", adminPwd) diff --git a/pkg/splunk/enterprise/licensemanager.go b/pkg/splunk/enterprise/licensemanager.go index 1dfd4edd7..953832342 100644 --- a/pkg/splunk/enterprise/licensemanager.go +++ b/pkg/splunk/enterprise/licensemanager.go @@ -23,7 +23,7 @@ import ( enterpriseApi "github.com/splunk/splunk-operator/api/v4" splutil "github.com/splunk/splunk-operator/pkg/splunk/util" - + splclient "github.com/splunk/splunk-operator/pkg/splunk/client" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/types" @@ -139,6 +139,16 @@ func ApplyLicenseManager(ctx context.Context, client splcommon.ControllerClient, return result, err } + + if cr.Spec.VaultIntegration.Enable { + //The InjectVaultSecret function is responsible for injecting secrets from HashiCorp Vault into the specified pod template. + splclient.InjectVaultSecret(ctx, client, statefulSet, &cr.Spec.VaultIntegration) + err := splclient.CheckAndRestartStatefulSet(ctx, client, statefulSet, &cr.Spec.VaultIntegration) + if err != nil { + return result, err + } + } + mgr := splctrl.DefaultStatefulSetPodManager{} phase, err := mgr.Update(ctx, client, statefulSet, 1) if err != nil { diff --git a/pkg/splunk/enterprise/monitoringconsole.go b/pkg/splunk/enterprise/monitoringconsole.go index 83af4a482..0864524e9 100644 --- a/pkg/splunk/enterprise/monitoringconsole.go +++ b/pkg/splunk/enterprise/monitoringconsole.go @@ -24,7 +24,7 @@ import ( "time" enterpriseApi "github.com/splunk/splunk-operator/api/v4" - + splclient "github.com/splunk/splunk-operator/pkg/splunk/client" splcommon "github.com/splunk/splunk-operator/pkg/splunk/common" splctrl "github.com/splunk/splunk-operator/pkg/splunk/controller" splutil "github.com/splunk/splunk-operator/pkg/splunk/util" @@ -147,6 +147,15 @@ func ApplyMonitoringConsole(ctx context.Context, client splcommon.ControllerClie return result, err } + if cr.Spec.VaultIntegration.Enable { + //The InjectVaultSecret function is responsible for injecting secrets from HashiCorp Vault into the specified pod template. + splclient.InjectVaultSecret(ctx, client, statefulSet, &cr.Spec.VaultIntegration) + err := splclient.CheckAndRestartStatefulSet(ctx, client, statefulSet, &cr.Spec.VaultIntegration) + if err != nil { + return result, err + } + } + mgr := splctrl.DefaultStatefulSetPodManager{} phase, err := mgr.Update(ctx, client, statefulSet, 1) if err != nil { diff --git a/pkg/splunk/enterprise/searchheadcluster.go b/pkg/splunk/enterprise/searchheadcluster.go index 6cbef29f3..0fdbe0db7 100644 --- a/pkg/splunk/enterprise/searchheadcluster.go +++ b/pkg/splunk/enterprise/searchheadcluster.go @@ -171,6 +171,15 @@ func ApplySearchHeadCluster(ctx context.Context, client splcommon.ControllerClie return result, err } + if cr.Spec.VaultIntegration.Enable { + //The InjectVaultSecret function is responsible for injecting secrets from HashiCorp Vault into the specified pod template. + splclient.InjectVaultSecret(ctx, client, statefulSet, &cr.Spec.VaultIntegration) + err := splclient.CheckAndRestartStatefulSet(ctx, client, statefulSet, &cr.Spec.VaultIntegration) + if err != nil { + return result, err + } + } + deployerManager := splctrl.DefaultStatefulSetPodManager{} phase, err := deployerManager.Update(ctx, client, statefulSet, 1) if err != nil { @@ -190,6 +199,15 @@ func ApplySearchHeadCluster(ctx context.Context, client splcommon.ControllerClie return result, err } + if cr.Spec.VaultIntegration.Enable { + //The InjectVaultSecret function is responsible for injecting secrets from HashiCorp Vault into the specified pod template. + splclient.InjectVaultSecret(ctx, client, statefulSet, &cr.Spec.VaultIntegration) + err := splclient.CheckAndRestartStatefulSet(ctx, client, statefulSet, &cr.Spec.VaultIntegration) + if err != nil { + return result, err + } + } + mgr := newSearchHeadClusterPodManager(client, scopedLog, cr, namespaceScopedSecret, splclient.NewSplunkClient) phase, err = mgr.Update(ctx, client, statefulSet, cr.Spec.Replicas) if err != nil { @@ -255,6 +273,7 @@ type searchHeadClusterPodManager struct { cr *enterpriseApi.SearchHeadCluster secrets *corev1.Secret newSplunkClient func(managementURI, username, password string) *splclient.SplunkClient + vaultIntegration *enterpriseApi.VaultIntegration } // newSerachHeadClusterPodManager function to create pod manager this is added to write unit test case @@ -265,6 +284,7 @@ var newSearchHeadClusterPodManager = func(client splcommon.ControllerClient, log secrets: secret, newSplunkClient: newSplunkClient, c: client, + vaultIntegration: &cr.Spec.VaultIntegration, } } @@ -447,9 +467,12 @@ func (mgr *searchHeadClusterPodManager) Update(ctx context.Context, c splcommon. podExecClient := splutil.GetPodExecClient(mgr.c, mgr.cr, "") // Check if a recycle of shc pods is necessary(due to shc_secret mismatch with namespace scoped secret) - err = ApplyShcSecret(ctx, mgr, desiredReplicas, podExecClient) - if err != nil { - return enterpriseApi.PhaseError, err + if !mgr.vaultIntegration.Enable { + err = ApplyShcSecret(ctx, mgr, desiredReplicas, podExecClient) + if err != nil { + return enterpriseApi.PhaseError, err + } + // FIXME here TODO add mgr.secrets somehow from vault to get password so shc rest call can be called } // update CR status with SHC information @@ -555,10 +578,19 @@ func (mgr *searchHeadClusterPodManager) getClient(ctx context.Context, n int32) fqdnName := splcommon.GetServiceFQDN(mgr.cr.GetNamespace(), fmt.Sprintf("%s.%s", memberName, GetSplunkServiceName(SplunkSearchHead, mgr.cr.GetName(), true))) - // Retrieve admin password from Pod - adminPwd, err := splutil.GetSpecificSecretTokenFromPod(ctx, mgr.c, memberName, mgr.cr.GetNamespace(), "password") - if err != nil { - scopedLog.Error(err, "Couldn't retrieve the admin password from Pod") + var adminPwd string + var err error + if mgr.vaultIntegration != nil && mgr.vaultIntegration.Enable { + adminPwd, err = splclient.GetSpecificSecretTokenFromVault(ctx, mgr.c, mgr.vaultIntegration, "password") + if err != nil { + scopedLog.Error(err, "Couldn't retrieve the admin password from Pod") + } + } else { + // Retrieve admin password from Pod + adminPwd, err = splutil.GetSpecificSecretTokenFromPod(ctx, mgr.c, memberName, mgr.cr.GetNamespace(), "password") + if err != nil { + scopedLog.Error(err, "Couldn't retrieve the admin password from Pod") + } } return mgr.newSplunkClient(fmt.Sprintf("https://%s:8089", fqdnName), "admin", adminPwd)