diff --git a/cmd/manager/main.go b/cmd/manager/main.go index b9adbd6f41b..b0bf053bbc7 100644 --- a/cmd/manager/main.go +++ b/cmd/manager/main.go @@ -168,6 +168,11 @@ func init() { "", fmt.Sprintf("K8s secret mounted into the path designated by %s to be used for webhook certificates", operator.WebhookCertDirFlag), ) + Cmd.Flags().Bool( + operator.SetDefaultFsGroupFlag, + true, + "Enables setting the default filesystem group in Pods security context", + ) // enable using dashed notation in flags and underscores in env viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_")) @@ -316,6 +321,7 @@ func execute() { RotateBefore: certRotateBefore, }, MaxConcurrentReconciles: viper.GetInt(operator.MaxConcurrentReconcilesFlag), + SetDefaultFsGroup: viper.GetBool(operator.SetDefaultFsGroupFlag), Tracer: tracer, } diff --git a/pkg/controller/common/defaults/pod_template.go b/pkg/controller/common/defaults/pod_template.go index d5eca116933..e42f6f12d94 100644 --- a/pkg/controller/common/defaults/pod_template.go +++ b/pkg/controller/common/defaults/pod_template.go @@ -11,6 +11,7 @@ import ( "github.com/elastic/cloud-on-k8s/pkg/controller/elasticsearch/settings" "github.com/elastic/cloud-on-k8s/pkg/utils/maps" + "github.com/elastic/cloud-on-k8s/pkg/utils/pointer" ) // PodDownwardEnvVars returns default environment variables created from the downward API. @@ -378,3 +379,13 @@ func (b *PodTemplateBuilder) WithAutomountServiceAccountToken() *PodTemplateBuil } return b } + +func (b *PodTemplateBuilder) WithFsGroup(defaultFsGroup int64) *PodTemplateBuilder { + if b.PodTemplate.Spec.SecurityContext == nil { + b.PodTemplate.Spec.SecurityContext = &corev1.PodSecurityContext{} + } + if b.PodTemplate.Spec.SecurityContext.FSGroup == nil { + b.PodTemplate.Spec.SecurityContext.FSGroup = pointer.Int64(defaultFsGroup) + } + return b +} diff --git a/pkg/controller/common/operator/flags.go b/pkg/controller/common/operator/flags.go index 6cb2358b66a..090b49a705b 100644 --- a/pkg/controller/common/operator/flags.go +++ b/pkg/controller/common/operator/flags.go @@ -20,6 +20,7 @@ const ( MetricsPortFlag = "metrics-port" NamespacesFlag = "namespaces" OperatorNamespaceFlag = "operator-namespace" + SetDefaultFsGroupFlag = "set-default-fsgroup" WebhookCertDirFlag = "webhook-cert-dir" WebhookSecretFlag = "webhook-secret" ) diff --git a/pkg/controller/common/operator/parameters.go b/pkg/controller/common/operator/parameters.go index c29e4eb9e26..a7c3c062b47 100644 --- a/pkg/controller/common/operator/parameters.go +++ b/pkg/controller/common/operator/parameters.go @@ -26,6 +26,9 @@ type Parameters struct { CertRotation certificates.RotationParams // MaxConcurrentReconciles controls the number of goroutines per controller. MaxConcurrentReconciles int + // SetDefaultFsGroup determines whether the operator should set the default + // filesystem group for Pod security context. + SetDefaultFsGroup bool // Tracer is a shared APM tracer instance or nil Tracer *apm.Tracer } diff --git a/pkg/controller/elasticsearch/driver/nodes.go b/pkg/controller/elasticsearch/driver/nodes.go index 75e2618b43b..d1108a54208 100644 --- a/pkg/controller/elasticsearch/driver/nodes.go +++ b/pkg/controller/elasticsearch/driver/nodes.go @@ -54,7 +54,7 @@ func (d *defaultDriver) reconcileNodeSpecs( return results.WithError(err) } - expectedResources, err := nodespec.BuildExpectedResources(d.ES, keystoreResources, actualStatefulSets) + expectedResources, err := nodespec.BuildExpectedResources(d.ES, keystoreResources, actualStatefulSets, d.OperatorParameters.SetDefaultFsGroup) if err != nil { return results.WithError(err) } diff --git a/pkg/controller/elasticsearch/nodespec/podspec.go b/pkg/controller/elasticsearch/nodespec/podspec.go index 96b692a48c8..01353a6103d 100644 --- a/pkg/controller/elasticsearch/nodespec/podspec.go +++ b/pkg/controller/elasticsearch/nodespec/podspec.go @@ -25,12 +25,17 @@ import ( "github.com/elastic/cloud-on-k8s/pkg/utils/k8s" ) +const ( + defaultFsGroup = 1000 +) + // BuildPodTemplateSpec builds a new PodTemplateSpec for an Elasticsearch node. func BuildPodTemplateSpec( es esv1.Elasticsearch, nodeSet esv1.NodeSet, cfg settings.CanonicalConfig, keystoreResources *keystore.Resources, + setDefaultFsGroup bool, ) (corev1.PodTemplateSpec, error) { volumes, volumeMounts := buildVolumes(es.Name, nodeSet, keystoreResources) labels, err := buildLabels(es, cfg, nodeSet, keystoreResources) @@ -52,6 +57,10 @@ func BuildPodTemplateSpec( } defaultContainerPorts := getDefaultContainerPorts(es) + if setDefaultFsGroup { + builder = builder.WithFsGroup(defaultFsGroup) + } + builder = builder. WithResources(DefaultResources). WithTerminationGracePeriod(DefaultTerminationGracePeriodSeconds). diff --git a/pkg/controller/elasticsearch/nodespec/podspec_test.go b/pkg/controller/elasticsearch/nodespec/podspec_test.go index 3997d87dcc8..6abbacfc921 100644 --- a/pkg/controller/elasticsearch/nodespec/podspec_test.go +++ b/pkg/controller/elasticsearch/nodespec/podspec_test.go @@ -87,6 +87,19 @@ var sampleES = esv1.Elasticsearch{ }, } +func TestBuildPodTemplateSpecWithDefaultFsGroup(t *testing.T) { + nodeSet := sampleES.Spec.NodeSets[0] + ver, err := version.Parse(sampleES.Spec.Version) + require.NoError(t, err) + cfg, err := settings.NewMergedESConfig(sampleES.Name, *ver, sampleES.Spec.HTTP, *nodeSet.Config) + require.NoError(t, err) + + actual, err := BuildPodTemplateSpec(sampleES, sampleES.Spec.NodeSets[0], cfg, nil, true) + require.NoError(t, err) + + require.Equal(t, int64(1000), *actual.Spec.SecurityContext.FSGroup) +} + func TestBuildPodTemplateSpec(t *testing.T) { nodeSet := sampleES.Spec.NodeSets[0] ver, err := version.Parse(sampleES.Spec.Version) @@ -94,7 +107,7 @@ func TestBuildPodTemplateSpec(t *testing.T) { cfg, err := settings.NewMergedESConfig(sampleES.Name, *ver, sampleES.Spec.HTTP, *nodeSet.Config) require.NoError(t, err) - actual, err := BuildPodTemplateSpec(sampleES, sampleES.Spec.NodeSets[0], cfg, nil) + actual, err := BuildPodTemplateSpec(sampleES, sampleES.Spec.NodeSets[0], cfg, nil, false) require.NoError(t, err) // build expected PodTemplateSpec diff --git a/pkg/controller/elasticsearch/nodespec/resources.go b/pkg/controller/elasticsearch/nodespec/resources.go index 7c989f9b0fc..9514e406978 100644 --- a/pkg/controller/elasticsearch/nodespec/resources.go +++ b/pkg/controller/elasticsearch/nodespec/resources.go @@ -38,6 +38,7 @@ func BuildExpectedResources( es esv1.Elasticsearch, keystoreResources *keystore.Resources, existingStatefulSets sset.StatefulSetList, + setDefaultFsGroup bool, ) (ResourcesList, error) { nodesResources := make(ResourcesList, 0, len(es.Spec.NodeSets)) @@ -58,7 +59,7 @@ func BuildExpectedResources( } // build stateful set and associated headless service - statefulSet, err := BuildStatefulSet(es, nodeSpec, cfg, keystoreResources, existingStatefulSets) + statefulSet, err := BuildStatefulSet(es, nodeSpec, cfg, keystoreResources, existingStatefulSets, setDefaultFsGroup) if err != nil { return nil, err } diff --git a/pkg/controller/elasticsearch/nodespec/statefulset.go b/pkg/controller/elasticsearch/nodespec/statefulset.go index f5507bb4be8..1327675c1a7 100644 --- a/pkg/controller/elasticsearch/nodespec/statefulset.go +++ b/pkg/controller/elasticsearch/nodespec/statefulset.go @@ -64,6 +64,7 @@ func BuildStatefulSet( cfg settings.CanonicalConfig, keystoreResources *keystore.Resources, existingStatefulSets sset.StatefulSetList, + setDefaultFsGroup bool, ) (appsv1.StatefulSet, error) { statefulSetName := esv1.StatefulSet(es.Name, nodeSet.Name) @@ -75,7 +76,7 @@ func BuildStatefulSet( nodeSet.VolumeClaimTemplates, nodeSet.PodTemplate.Spec, esvolume.DefaultVolumeClaimTemplates..., ) // build pod template - podTemplate, err := BuildPodTemplateSpec(es, nodeSet, cfg, keystoreResources) + podTemplate, err := BuildPodTemplateSpec(es, nodeSet, cfg, keystoreResources, setDefaultFsGroup) if err != nil { return appsv1.StatefulSet{}, err }