Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions docs/Install.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,19 @@ environment variable to the operator's deployment spec:
value: "mydomain.com"
```

## External TLS for Splunkd and HEC

By default Splunk will enable SSL/TLS for splunkd (8089) and hec (8088). It can be desirable
to offload encryption between pods to a the network layer using CNI and externally using Ingress
or Istio. The following environment variables can be added to the operator deployment to disable
default SSL/TLS

```yaml
- name: SPLUNKD_SSL_ENABLE
value: "false"
- name: SPLUNK_HEC_SSL
value: "false"
```

## Installing Splunk Operator

Expand Down
11 changes: 11 additions & 0 deletions pkg/splunk/common/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,17 @@ func GetServiceFQDN(namespace string, name string) string {
)
}

// GetServiceURI returns the fully qualified domain name for a Kubernetes service as URI.
func GetServiceURI(namespace string, name string) string {
var scheme string = "https"
if os.Getenv("SPLUNKD_SSL_ENABLE") == "false" {
scheme = "http"
}
return fmt.Sprintf(
"%s://%s:8089", scheme, GetServiceFQDN(namespace, name),
)
}

// GenerateSecret returns a randomly generated sequence of text that is n bytes in length.
func GenerateSecret(SecretBytes string, n int) []byte {
b := make([]byte, n)
Expand Down
18 changes: 18 additions & 0 deletions pkg/splunk/common/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,12 +144,30 @@ func TestGetServiceFQDN(t *testing.T) {
}
}

os.Setenv("CLUSTER_DOMAIN", "cluster.local")
test("test", "t1", "t1.test.svc.cluster.local")

os.Setenv("CLUSTER_DOMAIN", "example.com")
test("test", "t2", "t2.test.svc.example.com")
}

func TestGetServiceURI(t *testing.T) {
test := func(namespace string, name string, want string) {
got := GetServiceURI(namespace, name)
if got != want {
t.Errorf("GetServiceURI() = %s; want %s", got, want)
}
}

os.Setenv("CLUSTER_DOMAIN", "cluster.local")
os.Setenv("SPLUNKD_SSL_ENABLE", "true")
test("test", "t1", "https://t1.test.svc.cluster.local:8089")

os.Setenv("CLUSTER_DOMAIN", "cluster.local")
os.Setenv("SPLUNKD_SSL_ENABLE", "false")
test("test", "t2", "http://t2.test.svc.cluster.local:8089")
}

func TestGenerateSecret(t *testing.T) {
test := func(SecretBytes string, n int) {
results := [][]byte{}
Expand Down
5 changes: 2 additions & 3 deletions pkg/splunk/enterprise/clustermaster.go
Original file line number Diff line number Diff line change
Expand Up @@ -265,10 +265,9 @@ func PushMasterAppsBundle(c splcommon.ControllerClient, cr *enterprisev1.Cluster
scopedLog.Info("Issuing REST call to push master aps bundle")

masterIdxcName := cr.GetName()
fqdnName := splcommon.GetServiceFQDN(cr.GetNamespace(), GetSplunkServiceName(SplunkClusterMaster, masterIdxcName, false))
uri := splcommon.GetServiceURI(cr.GetNamespace(), GetSplunkServiceName(SplunkClusterMaster, masterIdxcName, false))

// Get a Splunk client to execute the REST call
splunkClient := splclient.NewSplunkClient(fmt.Sprintf("https://%s:8089", fqdnName), "admin", string(adminPwd))
splunkClient := splclient.NewSplunkClient(uri, "admin", string(adminPwd))

return splunkClient.BundlePush(true)
}
2 changes: 1 addition & 1 deletion pkg/splunk/enterprise/clustermaster_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ func TestGetClusterMasterStatefulSet(t *testing.T) {

cr.Spec.LicenseMasterRef.Name = "stack1"
cr.Spec.LicenseMasterRef.Namespace = "test"
test(`{"kind":"StatefulSet","apiVersion":"apps/v1","metadata":{"name":"splunk-stack1-cluster-master","namespace":"test","creationTimestamp":null,"ownerReferences":[{"apiVersion":"","kind":"","name":"stack1","uid":"","controller":true}]},"spec":{"replicas":1,"selector":{"matchLabels":{"app.kubernetes.io/component":"indexer","app.kubernetes.io/instance":"splunk-stack1-cluster-master","app.kubernetes.io/managed-by":"splunk-operator","app.kubernetes.io/name":"cluster-master","app.kubernetes.io/part-of":"splunk-stack1-indexer"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app.kubernetes.io/component":"indexer","app.kubernetes.io/instance":"splunk-stack1-cluster-master","app.kubernetes.io/managed-by":"splunk-operator","app.kubernetes.io/name":"cluster-master","app.kubernetes.io/part-of":"splunk-stack1-indexer"},"annotations":{"traffic.sidecar.istio.io/excludeOutboundPorts":"8089,8191,9997,7777,9000,17000,17500,19000","traffic.sidecar.istio.io/includeInboundPorts":"8000"}},"spec":{"volumes":[{"name":"mnt-splunk-secrets","secret":{"secretName":"splunk-stack1-cluster-master-secret-v1","defaultMode":420}}],"containers":[{"name":"splunk","image":"splunk/splunk","ports":[{"name":"http-splunkweb","containerPort":8000,"protocol":"TCP"},{"name":"https-splunkd","containerPort":8089,"protocol":"TCP"}],"env":[{"name":"SPLUNK_HOME","value":"/opt/splunk"},{"name":"SPLUNK_START_ARGS","value":"--accept-license"},{"name":"SPLUNK_DEFAULTS_URL","value":"/mnt/splunk-secrets/default.yml"},{"name":"SPLUNK_HOME_OWNERSHIP_ENFORCEMENT","value":"false"},{"name":"SPLUNK_ROLE","value":"splunk_cluster_master"},{"name":"SPLUNK_DECLARATIVE_ADMIN_PASSWORD","value":"true"},{"name":"SPLUNK_LICENSE_MASTER_URL","value":"splunk-stack1-license-master-service.test.svc.cluster.local"},{"name":"SPLUNK_CLUSTER_MASTER_URL","value":"localhost"}],"resources":{"limits":{"cpu":"4","memory":"8Gi"},"requests":{"cpu":"100m","memory":"512Mi"}},"volumeMounts":[{"name":"pvc-etc","mountPath":"/opt/splunk/etc"},{"name":"pvc-var","mountPath":"/opt/splunk/var"},{"name":"mnt-splunk-secrets","mountPath":"/mnt/splunk-secrets"}],"livenessProbe":{"exec":{"command":["/sbin/checkstate.sh"]},"initialDelaySeconds":300,"timeoutSeconds":30,"periodSeconds":30},"readinessProbe":{"exec":{"command":["/bin/grep","started","/opt/container_artifact/splunk-container.state"]},"initialDelaySeconds":10,"timeoutSeconds":5,"periodSeconds":5},"imagePullPolicy":"IfNotPresent"}],"securityContext":{"runAsUser":41812,"fsGroup":41812},"affinity":{"podAntiAffinity":{"preferredDuringSchedulingIgnoredDuringExecution":[{"weight":100,"podAffinityTerm":{"labelSelector":{"matchExpressions":[{"key":"app.kubernetes.io/instance","operator":"In","values":["splunk-stack1-cluster-master"]}]},"topologyKey":"kubernetes.io/hostname"}}]}},"schedulerName":"default-scheduler"}},"volumeClaimTemplates":[{"metadata":{"name":"pvc-etc","namespace":"test","creationTimestamp":null,"labels":{"app.kubernetes.io/component":"indexer","app.kubernetes.io/instance":"splunk-stack1-cluster-master","app.kubernetes.io/managed-by":"splunk-operator","app.kubernetes.io/name":"cluster-master","app.kubernetes.io/part-of":"splunk-stack1-indexer"}},"spec":{"accessModes":["ReadWriteOnce"],"resources":{"requests":{"storage":"10Gi"}}},"status":{}},{"metadata":{"name":"pvc-var","namespace":"test","creationTimestamp":null,"labels":{"app.kubernetes.io/component":"indexer","app.kubernetes.io/instance":"splunk-stack1-cluster-master","app.kubernetes.io/managed-by":"splunk-operator","app.kubernetes.io/name":"cluster-master","app.kubernetes.io/part-of":"splunk-stack1-indexer"}},"spec":{"accessModes":["ReadWriteOnce"],"resources":{"requests":{"storage":"100Gi"}}},"status":{}}],"serviceName":"splunk-stack1-cluster-master-headless","podManagementPolicy":"Parallel","updateStrategy":{"type":"OnDelete"}},"status":{"replicas":0}}`)
test(`{"kind":"StatefulSet","apiVersion":"apps/v1","metadata":{"name":"splunk-stack1-cluster-master","namespace":"test","creationTimestamp":null,"ownerReferences":[{"apiVersion":"","kind":"","name":"stack1","uid":"","controller":true}]},"spec":{"replicas":1,"selector":{"matchLabels":{"app.kubernetes.io/component":"indexer","app.kubernetes.io/instance":"splunk-stack1-cluster-master","app.kubernetes.io/managed-by":"splunk-operator","app.kubernetes.io/name":"cluster-master","app.kubernetes.io/part-of":"splunk-stack1-indexer"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app.kubernetes.io/component":"indexer","app.kubernetes.io/instance":"splunk-stack1-cluster-master","app.kubernetes.io/managed-by":"splunk-operator","app.kubernetes.io/name":"cluster-master","app.kubernetes.io/part-of":"splunk-stack1-indexer"},"annotations":{"traffic.sidecar.istio.io/excludeOutboundPorts":"8089,8191,9997,7777,9000,17000,17500,19000","traffic.sidecar.istio.io/includeInboundPorts":"8000"}},"spec":{"volumes":[{"name":"mnt-splunk-secrets","secret":{"secretName":"splunk-stack1-cluster-master-secret-v1","defaultMode":420}}],"containers":[{"name":"splunk","image":"splunk/splunk","ports":[{"name":"http-splunkweb","containerPort":8000,"protocol":"TCP"},{"name":"https-splunkd","containerPort":8089,"protocol":"TCP"}],"env":[{"name":"SPLUNK_HOME","value":"/opt/splunk"},{"name":"SPLUNK_START_ARGS","value":"--accept-license"},{"name":"SPLUNK_DEFAULTS_URL","value":"/mnt/splunk-secrets/default.yml"},{"name":"SPLUNK_HOME_OWNERSHIP_ENFORCEMENT","value":"false"},{"name":"SPLUNK_ROLE","value":"splunk_cluster_master"},{"name":"SPLUNK_DECLARATIVE_ADMIN_PASSWORD","value":"true"},{"name":"SPLUNK_LICENSE_MASTER_URL","value":"https://splunk-stack1-license-master-service.test.svc.cluster.local:8089"},{"name":"SPLUNK_CLUSTER_MASTER_URL","value":"localhost"}],"resources":{"limits":{"cpu":"4","memory":"8Gi"},"requests":{"cpu":"100m","memory":"512Mi"}},"volumeMounts":[{"name":"pvc-etc","mountPath":"/opt/splunk/etc"},{"name":"pvc-var","mountPath":"/opt/splunk/var"},{"name":"mnt-splunk-secrets","mountPath":"/mnt/splunk-secrets"}],"livenessProbe":{"exec":{"command":["/sbin/checkstate.sh"]},"initialDelaySeconds":300,"timeoutSeconds":30,"periodSeconds":30},"readinessProbe":{"exec":{"command":["/bin/grep","started","/opt/container_artifact/splunk-container.state"]},"initialDelaySeconds":10,"timeoutSeconds":5,"periodSeconds":5},"imagePullPolicy":"IfNotPresent"}],"securityContext":{"runAsUser":41812,"fsGroup":41812},"affinity":{"podAntiAffinity":{"preferredDuringSchedulingIgnoredDuringExecution":[{"weight":100,"podAffinityTerm":{"labelSelector":{"matchExpressions":[{"key":"app.kubernetes.io/instance","operator":"In","values":["splunk-stack1-cluster-master"]}]},"topologyKey":"kubernetes.io/hostname"}}]}},"schedulerName":"default-scheduler"}},"volumeClaimTemplates":[{"metadata":{"name":"pvc-etc","namespace":"test","creationTimestamp":null,"labels":{"app.kubernetes.io/component":"indexer","app.kubernetes.io/instance":"splunk-stack1-cluster-master","app.kubernetes.io/managed-by":"splunk-operator","app.kubernetes.io/name":"cluster-master","app.kubernetes.io/part-of":"splunk-stack1-indexer"}},"spec":{"accessModes":["ReadWriteOnce"],"resources":{"requests":{"storage":"10Gi"}}},"status":{}},{"metadata":{"name":"pvc-var","namespace":"test","creationTimestamp":null,"labels":{"app.kubernetes.io/component":"indexer","app.kubernetes.io/instance":"splunk-stack1-cluster-master","app.kubernetes.io/managed-by":"splunk-operator","app.kubernetes.io/name":"cluster-master","app.kubernetes.io/part-of":"splunk-stack1-indexer"}},"spec":{"accessModes":["ReadWriteOnce"],"resources":{"requests":{"storage":"100Gi"}}},"status":{}}],"serviceName":"splunk-stack1-cluster-master-headless","podManagementPolicy":"Parallel","updateStrategy":{"type":"OnDelete"}},"status":{"replicas":0}}`)

cr.Spec.LicenseMasterRef.Name = ""
cr.Spec.LicenseURL = "/mnt/splunk.lic"
Expand Down
30 changes: 25 additions & 5 deletions pkg/splunk/enterprise/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package enterprise

import (
"fmt"
"os"

appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
Expand Down Expand Up @@ -653,6 +654,22 @@ func updateSplunkPodTemplateWithConfig(client splcommon.ControllerClient, podTem
{Name: "SPLUNK_ROLE", Value: role},
{Name: "SPLUNK_DECLARATIVE_ADMIN_PASSWORD", Value: "true"},
}
if os.Getenv("SPLUNKD_SSL_ENABLE") == "false" {
env = append(env, corev1.EnvVar{
Name: "SPLUNK_CERT_PREFIX",
Value: "http",
})
env = append(env, corev1.EnvVar{
Name: "SPLUNKD_SSL_ENABLE",
Value: "false",
})
}
if os.Getenv("SPLUNK_HEC_SSL") == "false" {
env = append(env, corev1.EnvVar{
Name: "SPLUNK_HEC_SSL",
Value: "false",
})
}

// update variables for licensing, if configured
if spec.LicenseURL != "" {
Expand All @@ -662,13 +679,16 @@ func updateSplunkPodTemplateWithConfig(client splcommon.ControllerClient, podTem
})
}
if instanceType != SplunkLicenseMaster && spec.LicenseMasterRef.Name != "" {
licenseMasterURL := GetSplunkServiceName(SplunkLicenseMaster, spec.LicenseMasterRef.Name, false)
if spec.LicenseMasterRef.Namespace != "" {
licenseMasterURL = splcommon.GetServiceFQDN(spec.LicenseMasterRef.Namespace, licenseMasterURL)
licenseMasterName := GetSplunkServiceName(SplunkLicenseMaster, spec.LicenseMasterRef.Name, false)
var namespace string
if spec.LicenseMasterRef.Namespace == "" {
namespace = cr.GetNamespace()
} else {
namespace = spec.LicenseMasterRef.Namespace
}
env = append(env, corev1.EnvVar{
Name: "SPLUNK_LICENSE_MASTER_URL",
Value: licenseMasterURL,
Value: splcommon.GetServiceURI(namespace, licenseMasterName),
})
}

Expand All @@ -680,7 +700,7 @@ func updateSplunkPodTemplateWithConfig(client splcommon.ControllerClient, podTem
} else if spec.ClusterMasterRef.Name != "" {
clusterMasterURL = GetSplunkServiceName(SplunkClusterMaster, spec.ClusterMasterRef.Name, false)
if spec.ClusterMasterRef.Namespace != "" {
clusterMasterURL = splcommon.GetServiceFQDN(spec.ClusterMasterRef.Namespace, clusterMasterURL)
clusterMasterURL = splcommon.GetServiceURI(spec.ClusterMasterRef.Namespace, clusterMasterURL)
}
}
if clusterMasterURL != "" {
Expand Down
12 changes: 6 additions & 6 deletions pkg/splunk/enterprise/indexercluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -445,8 +445,8 @@ func (mgr *indexerClusterPodManager) getClient(n int32) *splclient.SplunkClient
// Get Pod Name
memberName := GetSplunkStatefulsetPodName(SplunkIndexer, mgr.cr.GetName(), n)

// Get Fully Qualified Domain Name
fqdnName := splcommon.GetServiceFQDN(mgr.cr.GetNamespace(),
// Get uri
uri := splcommon.GetServiceURI(mgr.cr.GetNamespace(),
fmt.Sprintf("%s.%s", memberName, GetSplunkServiceName(SplunkIndexer, mgr.cr.GetName(), true)))

// Retrieve admin password from Pod
Expand All @@ -455,7 +455,7 @@ func (mgr *indexerClusterPodManager) getClient(n int32) *splclient.SplunkClient
scopedLog.Error(err, "Couldn't retrieve the admin password from pod")
}

return mgr.newSplunkClient(fmt.Sprintf("https://%s:8089", fqdnName), "admin", adminPwd)
return mgr.newSplunkClient(uri, "admin", adminPwd)
}

// getClusterMasterClient for indexerClusterPodManager returns a SplunkClient for cluster master
Expand All @@ -471,7 +471,7 @@ func (mgr *indexerClusterPodManager) getClusterMasterClient() *splclient.SplunkC
}

// Get Fully Qualified Domain Name
fqdnName := splcommon.GetServiceFQDN(mgr.cr.GetNamespace(), GetSplunkServiceName(SplunkClusterMaster, masterIdxcName, false))
uri := splcommon.GetServiceURI(mgr.cr.GetNamespace(), GetSplunkServiceName(SplunkClusterMaster, masterIdxcName, false))

// Retrieve admin password for Pod
podName := fmt.Sprintf("splunk-%s-cluster-master-0", masterIdxcName)
Expand All @@ -480,7 +480,7 @@ func (mgr *indexerClusterPodManager) getClusterMasterClient() *splclient.SplunkC
scopedLog.Error(err, "Couldn't retrieve the admin password from pod")
}

return mgr.newSplunkClient(fmt.Sprintf("https://%s:8089", fqdnName), "admin", adminPwd)
return mgr.newSplunkClient(uri, "admin", adminPwd)
}

// getSiteRepFactorOriginCount gets the origin count of the site_replication_factor
Expand All @@ -500,7 +500,7 @@ func (mgr *indexerClusterPodManager) verifyRFPeers(c splcommon.ControllerClient)
cm := mgr.getClusterMasterClient()
clusterInfo, err := cm.GetClusterInfo(false)
if err != nil {
return fmt.Errorf("Could not get cluster info from cluster master")
return fmt.Errorf("Could not get cluster info from cluster master: %s", err)
}
var replicationFactor int32
// if it is a multisite indexer cluster, check site_replication_factor
Expand Down
12 changes: 7 additions & 5 deletions pkg/splunk/enterprise/monitoringconsole.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ package enterprise

import (
"context"
"fmt"
"reflect"
"sort"
"strings"
Expand Down Expand Up @@ -110,14 +109,17 @@ func ApplyMonitoringConsole(client splcommon.ControllerClient, cr splcommon.Meta

// getMonitoringConsoleClient for monitoringConsolePodManager returns a SplunkClient for monitoring console
func (mgr *monitoringConsolePodManager) getMonitoringConsoleClient(cr splcommon.MetaObject) *splclient.SplunkClient {
fqdnName := splcommon.GetServiceFQDN(cr.GetNamespace(), GetSplunkServiceName(SplunkMonitoringConsole, cr.GetNamespace(), false))
return mgr.newSplunkClient(fmt.Sprintf("https://%s:8089", fqdnName), "admin", string(mgr.secrets.Data["password"]))
uri := splcommon.GetServiceURI(cr.GetNamespace(), GetSplunkServiceName(SplunkMonitoringConsole, cr.GetNamespace(), false))

return mgr.newSplunkClient(uri, "admin", string(mgr.secrets.Data["password"]))
}

// getClusterMasterClient for monitoringConsolePodManager returns a SplunkClient for cluster master
func (mgr *monitoringConsolePodManager) getClusterMasterClient(cr splcommon.MetaObject) *splclient.SplunkClient {
fqdnName := splcommon.GetServiceFQDN(cr.GetNamespace(), GetSplunkServiceName(SplunkClusterMaster, cr.GetName(), false))
return mgr.newSplunkClient(fmt.Sprintf("https://%s:8089", fqdnName), "admin", string(mgr.secrets.Data["password"]))
uri := splcommon.GetServiceURI(cr.GetNamespace(), GetSplunkServiceName(SplunkClusterMaster, cr.GetName(), false))

return mgr.newSplunkClient(uri, "admin", string(mgr.secrets.Data["password"]))

}

// monitoringConsolePodManager is used to manage the monitoring console pod
Expand Down
9 changes: 6 additions & 3 deletions pkg/splunk/enterprise/searchheadcluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -429,16 +429,19 @@ func (mgr *searchHeadClusterPodManager) getClient(n int32) *splclient.SplunkClie
memberName := GetSplunkStatefulsetPodName(SplunkSearchHead, mgr.cr.GetName(), n)

// Get Fully Qualified Domain Name
fqdnName := splcommon.GetServiceFQDN(mgr.cr.GetNamespace(),
fmt.Sprintf("%s.%s", memberName, GetSplunkServiceName(SplunkSearchHead, mgr.cr.GetName(), true)))
uri := splcommon.GetServiceURI(
mgr.cr.GetNamespace(),
fmt.Sprintf("%s.%s", memberName, GetSplunkServiceName(SplunkSearchHead, mgr.cr.GetName(), true)),
)

// Retrieve admin password from Pod
adminPwd, err := splutil.GetSpecificSecretTokenFromPod(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)
return mgr.newSplunkClient(uri, "admin", adminPwd)

}

// updateStatus for searchHeadClusterPodManager uses the REST API to update the status for a SearcHead custom resource
Expand Down
Loading