Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add set-default-security-context flag to handle runAs user in ES 8.0+ #3342

Merged
merged 7 commits into from
Aug 31, 2020
Merged
Show file tree
Hide file tree
Changes from 5 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
10 changes: 8 additions & 2 deletions cmd/manager/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,11 @@ func Command() *cobra.Command {
DefaultWebhookName,
"Name of the Kubernetes ValidatingWebhookConfiguration resource. Only used when enable-webhook is true.",
)
cmd.Flags().Bool(
operator.SetDefaultSecurityContextFlag,
true,
"Enables setting the default security context of Elasticsearch 8.0+ Pods, ignored pre-8.0",
david-kow marked this conversation as resolved.
Show resolved Hide resolved
)

// hide development mode flags from the usage message
_ = cmd.Flags().MarkHidden(operator.AutoPortForwardFlag)
Expand Down Expand Up @@ -455,8 +460,9 @@ func startOperator(stopChan <-chan struct{}) error {
Validity: certValidity,
RotateBefore: certRotateBefore,
},
MaxConcurrentReconciles: viper.GetInt(operator.MaxConcurrentReconcilesFlag),
Tracer: tracer,
MaxConcurrentReconciles: viper.GetInt(operator.MaxConcurrentReconcilesFlag),
SetDefaultSecurityContext: viper.GetBool(operator.SetDefaultSecurityContextFlag),
Tracer: tracer,
}

if viper.GetBool(operator.EnableWebhookFlag) {
Expand Down
43 changes: 22 additions & 21 deletions pkg/controller/common/operator/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,26 @@
package operator

const (
AutoPortForwardFlag = "auto-port-forward"
CACertRotateBeforeFlag = "ca-cert-rotate-before"
CACertValidityFlag = "ca-cert-validity"
CertRotateBeforeFlag = "cert-rotate-before"
CertValidityFlag = "cert-validity"
ConfigFlag = "config"
ContainerRegistryFlag = "container-registry"
ContainerSuffixFlag = "container-suffix"
DebugHTTPListenFlag = "debug-http-listen"
DisableConfigWatch = "disable-config-watch"
EnableTracingFlag = "enable-tracing"
EnableWebhookFlag = "enable-webhook"
EnforceRBACOnRefsFlag = "enforce-rbac-on-refs"
ManageWebhookCertsFlag = "manage-webhook-certs"
MaxConcurrentReconcilesFlag = "max-concurrent-reconciles"
MetricsPortFlag = "metrics-port"
NamespacesFlag = "namespaces"
OperatorNamespaceFlag = "operator-namespace"
WebhookCertDirFlag = "webhook-cert-dir"
WebhookNameFlag = "webhook-name"
WebhookSecretFlag = "webhook-secret"
AutoPortForwardFlag = "auto-port-forward"
CACertRotateBeforeFlag = "ca-cert-rotate-before"
CACertValidityFlag = "ca-cert-validity"
CertRotateBeforeFlag = "cert-rotate-before"
CertValidityFlag = "cert-validity"
ConfigFlag = "config"
ContainerRegistryFlag = "container-registry"
ContainerSuffixFlag = "container-suffix"
DebugHTTPListenFlag = "debug-http-listen"
DisableConfigWatch = "disable-config-watch"
EnableTracingFlag = "enable-tracing"
EnableWebhookFlag = "enable-webhook"
EnforceRBACOnRefsFlag = "enforce-rbac-on-refs"
ManageWebhookCertsFlag = "manage-webhook-certs"
MaxConcurrentReconcilesFlag = "max-concurrent-reconciles"
MetricsPortFlag = "metrics-port"
NamespacesFlag = "namespaces"
OperatorNamespaceFlag = "operator-namespace"
SetDefaultSecurityContextFlag = "set-default-security-context"
WebhookCertDirFlag = "webhook-cert-dir"
WebhookNameFlag = "webhook-name"
WebhookSecretFlag = "webhook-secret"
)
3 changes: 3 additions & 0 deletions pkg/controller/common/operator/parameters.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ type Parameters struct {
CertRotation certificates.RotationParams
// MaxConcurrentReconciles controls the number of goroutines per controller.
MaxConcurrentReconciles int
// SetDefaultSecurityContext enables setting the default security context
// of Elasticsearch 8.0+ Pods, ignored pre-8.0
SetDefaultSecurityContext bool
// Tracer is a shared APM tracer instance or nil
Tracer *apm.Tracer
}
5 changes: 5 additions & 0 deletions pkg/controller/common/version/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ var (
SupportedBeatVersions = MinMaxVersion{Min: From(7, 0, 0), Max: From(8, 99, 99)}
)

var (
// MinDefaultSecurityContextVersion is the minimal version for which the operator will inspect set-default-security-context flag.
MinDefaultSecurityContextVersion = MustParse("8.0.0")
david-kow marked this conversation as resolved.
Show resolved Hide resolved
)

// MinMaxVersion holds the minimum and maximum supported versions.
type MinMaxVersion struct {
Min Version
Expand Down
2 changes: 1 addition & 1 deletion pkg/controller/elasticsearch/driver/nodes.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.SetDefaultSecurityContext)
if err != nil {
return results.WithError(err)
}
Expand Down
20 changes: 19 additions & 1 deletion pkg/controller/elasticsearch/nodespec/podspec.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ import (
"github.com/elastic/cloud-on-k8s/pkg/controller/elasticsearch/settings"
esvolume "github.com/elastic/cloud-on-k8s/pkg/controller/elasticsearch/volume"
"github.com/elastic/cloud-on-k8s/pkg/utils/k8s"
"github.com/elastic/cloud-on-k8s/pkg/utils/pointer"
)

const (
defaultFsGroup = 1000
)

// BuildPodTemplateSpec builds a new PodTemplateSpec for an Elasticsearch node.
Expand All @@ -31,6 +36,7 @@ func BuildPodTemplateSpec(
nodeSet esv1.NodeSet,
cfg settings.CanonicalConfig,
keystoreResources *keystore.Resources,
setDefaultSecurityContext bool,
) (corev1.PodTemplateSpec, error) {
volumes, volumeMounts := buildVolumes(es.Name, nodeSet, keystoreResources)
labels, err := buildLabels(es, cfg, nodeSet, keystoreResources)
Expand All @@ -49,7 +55,19 @@ func BuildPodTemplateSpec(
return corev1.PodTemplateSpec{}, err
}

builder := defaults.NewPodTemplateBuilder(nodeSet.PodTemplate, esv1.ElasticsearchContainerName).
builder := defaults.NewPodTemplateBuilder(nodeSet.PodTemplate, esv1.ElasticsearchContainerName)

ver, err := version.Parse(es.Spec.Version)
if err != nil {
return corev1.PodTemplateSpec{}, err
}
if ver.IsSameOrAfter(version.MinDefaultSecurityContextVersion) && setDefaultSecurityContext {
builder = builder.WithPodSecurityContext(corev1.PodSecurityContext{
FSGroup: pointer.Int64(defaultFsGroup),
})
}

builder = builder.
WithLabels(labels).
WithAnnotations(DefaultAnnotations).
WithDockerImage(es.Spec.Image, container.ImageRepository(container.ElasticsearchImage, es.Spec.Version)).
Expand Down
90 changes: 89 additions & 1 deletion pkg/controller/elasticsearch/nodespec/podspec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/elastic/cloud-on-k8s/pkg/controller/common/version"
"github.com/elastic/cloud-on-k8s/pkg/controller/elasticsearch/initcontainer"
"github.com/elastic/cloud-on-k8s/pkg/controller/elasticsearch/settings"
"github.com/elastic/cloud-on-k8s/pkg/utils/pointer"
"github.com/go-test/deep"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -87,14 +88,101 @@ var sampleES = esv1.Elasticsearch{
},
}

func TestBuildPodTemplateSpecWithDefaultSecurityContext(t *testing.T) {
for _, tt := range []struct {
name string
version version.Version
setDefaultFSGroup bool
userSecurityContext *corev1.PodSecurityContext
wantSecurityContext *corev1.PodSecurityContext
}{
{
name: "pre-8.0, setting off, no user context",
version: version.MustParse("7.8.0"),
setDefaultFSGroup: false,
userSecurityContext: nil,
wantSecurityContext: nil,
},
{
name: "pre-8.0, setting off, user context",
version: version.MustParse("7.8.0"),
setDefaultFSGroup: false,
userSecurityContext: &corev1.PodSecurityContext{FSGroup: pointer.Int64(123)},
wantSecurityContext: &corev1.PodSecurityContext{FSGroup: pointer.Int64(123)},
},
{
name: "pre-8.0, setting on, no user context",
version: version.MustParse("7.8.0"),
setDefaultFSGroup: true,
userSecurityContext: nil,
wantSecurityContext: nil,
},
{
name: "pre-8.0, setting on, user context",
version: version.MustParse("7.8.0"),
setDefaultFSGroup: true,
userSecurityContext: &corev1.PodSecurityContext{FSGroup: pointer.Int64(123)},
wantSecurityContext: &corev1.PodSecurityContext{FSGroup: pointer.Int64(123)},
},
{
name: "8.0+, setting off, no user context",
version: version.MustParse("8.0.0"),
setDefaultFSGroup: false,
userSecurityContext: nil,
wantSecurityContext: nil,
},
{
name: "8.0+, setting off, user context",
version: version.MustParse("8.0.0"),
setDefaultFSGroup: false,
userSecurityContext: &corev1.PodSecurityContext{FSGroup: pointer.Int64(123)},
wantSecurityContext: &corev1.PodSecurityContext{FSGroup: pointer.Int64(123)},
},
{
name: "8.0+, setting on, no user context",
version: version.MustParse("8.0.0"),
setDefaultFSGroup: true,
userSecurityContext: nil,
wantSecurityContext: &corev1.PodSecurityContext{FSGroup: pointer.Int64(1000)},
},
{
name: "8.0+, setting on, user context",
version: version.MustParse("8.0.0"),
setDefaultFSGroup: true,
userSecurityContext: &corev1.PodSecurityContext{FSGroup: pointer.Int64(123)},
wantSecurityContext: &corev1.PodSecurityContext{FSGroup: pointer.Int64(123)},
},
{
name: "8.0+, setting on, empty user context",
version: version.MustParse("8.0.0"),
setDefaultFSGroup: true,
userSecurityContext: &corev1.PodSecurityContext{},
wantSecurityContext: &corev1.PodSecurityContext{},
},
} {
t.Run(tt.name, func(t *testing.T) {
es := *sampleES.DeepCopy()
es.Spec.Version = tt.version.String()
es.Spec.NodeSets[0].PodTemplate.Spec.SecurityContext = tt.userSecurityContext

cfg, err := settings.NewMergedESConfig(es.Name, tt.version, es.Spec.HTTP, *es.Spec.NodeSets[0].Config)
require.NoError(t, err)

actual, err := BuildPodTemplateSpec(es, es.Spec.NodeSets[0], cfg, nil, tt.setDefaultFSGroup)
require.NoError(t, err)
require.Equal(t, tt.wantSecurityContext, actual.Spec.SecurityContext)
})
}
}

func TestBuildPodTemplateSpec(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)
actual, err := BuildPodTemplateSpec(sampleES, sampleES.Spec.NodeSets[0], cfg, nil, false)
require.NoError(t, err)

// build expected PodTemplateSpec
Expand Down
3 changes: 2 additions & 1 deletion pkg/controller/elasticsearch/nodespec/resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ func BuildExpectedResources(
es esv1.Elasticsearch,
keystoreResources *keystore.Resources,
existingStatefulSets sset.StatefulSetList,
setDefaultSecurityContext bool,
) (ResourcesList, error) {
nodesResources := make(ResourcesList, 0, len(es.Spec.NodeSets))

Expand All @@ -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, setDefaultSecurityContext)
if err != nil {
return nil, err
}
Expand Down
3 changes: 2 additions & 1 deletion pkg/controller/elasticsearch/nodespec/statefulset.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ func BuildStatefulSet(
cfg settings.CanonicalConfig,
keystoreResources *keystore.Resources,
existingStatefulSets sset.StatefulSetList,
setDefaultSecurityContext bool,
) (appsv1.StatefulSet, error) {
statefulSetName := esv1.StatefulSet(es.Name, nodeSet.Name)

Expand All @@ -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, setDefaultSecurityContext)
if err != nil {
return appsv1.StatefulSet{}, err
}
Expand Down