From 4506f4c2d054eac15fbb902ed12ca949339ad1e1 Mon Sep 17 00:00:00 2001 From: Yu-Ju Hong Date: Thu, 26 Jan 2017 18:22:13 -0800 Subject: [PATCH] securitycontext: move docker-specific logic into kubelet/dockertools This change moves the code specific to docker to kubelet/dockertools, while leaving the common utility functions at its current package (pkg/securitycontext). When we deprecate dockertools in the future, the code will be moved to pkg/kubelet/dockershim instead. --- pkg/kubelet/container/helpers.go | 15 + pkg/kubelet/dockershim/BUILD | 4 +- pkg/kubelet/dockershim/security_context.go | 2 +- .../dockershim/security_context_test.go | 2 +- pkg/kubelet/dockertools/BUILD | 6 +- pkg/kubelet/dockertools/docker_manager.go | 3 +- pkg/kubelet/dockertools/securitycontext/BUILD | 52 ++++ .../dockertools/securitycontext/doc.go | 18 ++ .../dockertools/securitycontext/fake.go | 35 +++ .../dockertools/securitycontext/provider.go | 117 ++++++++ .../securitycontext/provider_test.go | 0 .../dockertools}/securitycontext/types.go | 0 pkg/kubelet/rkt/rkt.go | 2 +- pkg/securitycontext/BUILD | 15 +- pkg/securitycontext/fake.go | 14 - pkg/securitycontext/provider.go | 274 ------------------ pkg/securitycontext/util.go | 144 +++++++++ 17 files changed, 395 insertions(+), 308 deletions(-) create mode 100644 pkg/kubelet/dockertools/securitycontext/BUILD create mode 100644 pkg/kubelet/dockertools/securitycontext/doc.go create mode 100644 pkg/kubelet/dockertools/securitycontext/fake.go create mode 100644 pkg/kubelet/dockertools/securitycontext/provider.go rename pkg/{ => kubelet/dockertools}/securitycontext/provider_test.go (100%) rename pkg/{ => kubelet/dockertools}/securitycontext/types.go (100%) delete mode 100644 pkg/securitycontext/provider.go diff --git a/pkg/kubelet/container/helpers.go b/pkg/kubelet/container/helpers.go index e9170713e4..ff1a51b515 100644 --- a/pkg/kubelet/container/helpers.go +++ b/pkg/kubelet/container/helpers.go @@ -283,3 +283,18 @@ func HasPrivilegedContainer(pod *v1.Pod) bool { } return false } + +// MakeCapabilities creates string slices from Capability slices +func MakeCapabilities(capAdd []v1.Capability, capDrop []v1.Capability) ([]string, []string) { + var ( + addCaps []string + dropCaps []string + ) + for _, cap := range capAdd { + addCaps = append(addCaps, string(cap)) + } + for _, cap := range capDrop { + dropCaps = append(dropCaps, string(cap)) + } + return addCaps, dropCaps +} diff --git a/pkg/kubelet/dockershim/BUILD b/pkg/kubelet/dockershim/BUILD index 03e8ce191e..77ac8e9fde 100644 --- a/pkg/kubelet/dockershim/BUILD +++ b/pkg/kubelet/dockershim/BUILD @@ -32,6 +32,7 @@ go_library( "//pkg/kubelet/container:go_default_library", "//pkg/kubelet/dockershim/cm:go_default_library", "//pkg/kubelet/dockertools:go_default_library", + "//pkg/kubelet/dockertools/securitycontext:go_default_library", "//pkg/kubelet/leaky:go_default_library", "//pkg/kubelet/network:go_default_library", "//pkg/kubelet/network/cni:go_default_library", @@ -40,7 +41,6 @@ go_library( "//pkg/kubelet/server/streaming:go_default_library", "//pkg/kubelet/types:go_default_library", "//pkg/kubelet/util/ioutils:go_default_library", - "//pkg/securitycontext:go_default_library", "//pkg/util/term:go_default_library", "//vendor:github.com/docker/engine-api/types", "//vendor:github.com/docker/engine-api/types/container", @@ -72,11 +72,11 @@ go_test( "//pkg/kubelet/container:go_default_library", "//pkg/kubelet/container/testing:go_default_library", "//pkg/kubelet/dockertools:go_default_library", + "//pkg/kubelet/dockertools/securitycontext:go_default_library", "//pkg/kubelet/network:go_default_library", "//pkg/kubelet/network/mock_network:go_default_library", "//pkg/kubelet/types:go_default_library", "//pkg/security/apparmor:go_default_library", - "//pkg/securitycontext:go_default_library", "//vendor:github.com/docker/engine-api/types", "//vendor:github.com/docker/engine-api/types/container", "//vendor:github.com/golang/mock/gomock", diff --git a/pkg/kubelet/dockershim/security_context.go b/pkg/kubelet/dockershim/security_context.go index b2203d6600..9e52131d84 100644 --- a/pkg/kubelet/dockershim/security_context.go +++ b/pkg/kubelet/dockershim/security_context.go @@ -24,8 +24,8 @@ import ( "k8s.io/kubernetes/pkg/api/v1" runtimeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime" + "k8s.io/kubernetes/pkg/kubelet/dockertools/securitycontext" "k8s.io/kubernetes/pkg/kubelet/network" - "k8s.io/kubernetes/pkg/securitycontext" ) // applySandboxSecurityContext updates docker sandbox options according to security context. diff --git a/pkg/kubelet/dockershim/security_context_test.go b/pkg/kubelet/dockershim/security_context_test.go index 0397283aae..386810859d 100644 --- a/pkg/kubelet/dockershim/security_context_test.go +++ b/pkg/kubelet/dockershim/security_context_test.go @@ -25,7 +25,7 @@ import ( "github.com/stretchr/testify/assert" runtimeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime" - "k8s.io/kubernetes/pkg/securitycontext" + "k8s.io/kubernetes/pkg/kubelet/dockertools/securitycontext" ) func TestModifyContainerConfig(t *testing.T) { diff --git a/pkg/kubelet/dockertools/BUILD b/pkg/kubelet/dockertools/BUILD index 9af2ed2225..68bf08f2ad 100644 --- a/pkg/kubelet/dockertools/BUILD +++ b/pkg/kubelet/dockertools/BUILD @@ -33,6 +33,7 @@ go_library( "//pkg/kubelet/cm:go_default_library", "//pkg/kubelet/container:go_default_library", "//pkg/kubelet/custommetrics:go_default_library", + "//pkg/kubelet/dockertools/securitycontext:go_default_library", "//pkg/kubelet/events:go_default_library", "//pkg/kubelet/images:go_default_library", "//pkg/kubelet/leaky:go_default_library", @@ -149,6 +150,9 @@ filegroup( filegroup( name = "all-srcs", - srcs = [":package-srcs"], + srcs = [ + ":package-srcs", + "//pkg/kubelet/dockertools/securitycontext:all-srcs", + ], tags = ["automanaged"], ) diff --git a/pkg/kubelet/dockertools/docker_manager.go b/pkg/kubelet/dockertools/docker_manager.go index ade66493bc..c769a78ca2 100644 --- a/pkg/kubelet/dockertools/docker_manager.go +++ b/pkg/kubelet/dockertools/docker_manager.go @@ -54,6 +54,7 @@ import ( "k8s.io/kubernetes/pkg/client/record" "k8s.io/kubernetes/pkg/kubelet/cm" kubecontainer "k8s.io/kubernetes/pkg/kubelet/container" + dockersecurity "k8s.io/kubernetes/pkg/kubelet/dockertools/securitycontext" "k8s.io/kubernetes/pkg/kubelet/events" "k8s.io/kubernetes/pkg/kubelet/images" "k8s.io/kubernetes/pkg/kubelet/lifecycle" @@ -819,7 +820,7 @@ func (dm *DockerManager) runContainer( glog.V(3).Infof("Container %v/%v/%v: setting entrypoint \"%v\" and command \"%v\"", pod.Namespace, pod.Name, container.Name, dockerOpts.Config.Entrypoint, dockerOpts.Config.Cmd) supplementalGids := dm.runtimeHelper.GetExtraSupplementalGroupsForPod(pod) - securityContextProvider := securitycontext.NewSimpleSecurityContextProvider() + securityContextProvider := dockersecurity.NewSimpleSecurityContextProvider() securityContextProvider.ModifyContainerConfig(pod, container, dockerOpts.Config) securityContextProvider.ModifyHostConfig(pod, container, dockerOpts.HostConfig, supplementalGids) createResp, err := dm.client.CreateContainer(dockerOpts) diff --git a/pkg/kubelet/dockertools/securitycontext/BUILD b/pkg/kubelet/dockertools/securitycontext/BUILD new file mode 100644 index 0000000000..cf85d15d6c --- /dev/null +++ b/pkg/kubelet/dockertools/securitycontext/BUILD @@ -0,0 +1,52 @@ +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +load( + "@io_bazel_rules_go//go:def.bzl", + "go_library", + "go_test", +) + +go_library( + name = "go_default_library", + srcs = [ + "doc.go", + "fake.go", + "provider.go", + "types.go", + ], + tags = ["automanaged"], + deps = [ + "//pkg/api/v1:go_default_library", + "//pkg/kubelet/container:go_default_library", + "//pkg/kubelet/leaky:go_default_library", + "//pkg/securitycontext:go_default_library", + "//vendor:github.com/docker/engine-api/types/container", + ], +) + +go_test( + name = "go_default_test", + srcs = ["provider_test.go"], + library = ":go_default_library", + tags = ["automanaged"], + deps = [ + "//pkg/api/testing:go_default_library", + "//pkg/api/v1:go_default_library", + "//vendor:github.com/docker/engine-api/types/container", + ], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], +) diff --git a/pkg/kubelet/dockertools/securitycontext/doc.go b/pkg/kubelet/dockertools/securitycontext/doc.go new file mode 100644 index 0000000000..72fde21567 --- /dev/null +++ b/pkg/kubelet/dockertools/securitycontext/doc.go @@ -0,0 +1,18 @@ +/* +Copyright 2014 The Kubernetes Authors. + +Licensed 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 securitycontext contains security context api implementations +package securitycontext // import "k8s.io/kubernetes/pkg/kubelet/dockertools/securitycontext" diff --git a/pkg/kubelet/dockertools/securitycontext/fake.go b/pkg/kubelet/dockertools/securitycontext/fake.go new file mode 100644 index 0000000000..9a2cf55730 --- /dev/null +++ b/pkg/kubelet/dockertools/securitycontext/fake.go @@ -0,0 +1,35 @@ +/* +Copyright 2014 The Kubernetes Authors. + +Licensed 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 securitycontext + +import ( + "k8s.io/kubernetes/pkg/api/v1" + + dockercontainer "github.com/docker/engine-api/types/container" +) + +// NewFakeSecurityContextProvider creates a new, no-op security context provider. +func NewFakeSecurityContextProvider() SecurityContextProvider { + return FakeSecurityContextProvider{} +} + +type FakeSecurityContextProvider struct{} + +func (p FakeSecurityContextProvider) ModifyContainerConfig(pod *v1.Pod, container *v1.Container, config *dockercontainer.Config) { +} +func (p FakeSecurityContextProvider) ModifyHostConfig(pod *v1.Pod, container *v1.Container, hostConfig *dockercontainer.HostConfig, supplementalGids []int64) { +} diff --git a/pkg/kubelet/dockertools/securitycontext/provider.go b/pkg/kubelet/dockertools/securitycontext/provider.go new file mode 100644 index 0000000000..c7a9201e98 --- /dev/null +++ b/pkg/kubelet/dockertools/securitycontext/provider.go @@ -0,0 +1,117 @@ +/* +Copyright 2014 The Kubernetes Authors. + +Licensed 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 securitycontext + +import ( + "fmt" + "strconv" + + "k8s.io/kubernetes/pkg/api/v1" + kubecontainer "k8s.io/kubernetes/pkg/kubelet/container" + "k8s.io/kubernetes/pkg/kubelet/leaky" + "k8s.io/kubernetes/pkg/securitycontext" + + dockercontainer "github.com/docker/engine-api/types/container" +) + +// NewSimpleSecurityContextProvider creates a new SimpleSecurityContextProvider. +func NewSimpleSecurityContextProvider() SecurityContextProvider { + return SimpleSecurityContextProvider{} +} + +// SimpleSecurityContextProvider is the default implementation of a SecurityContextProvider. +type SimpleSecurityContextProvider struct{} + +// ModifyContainerConfig is called before the Docker createContainer call. +// The security context provider can make changes to the Config with which +// the container is created. +func (p SimpleSecurityContextProvider) ModifyContainerConfig(pod *v1.Pod, container *v1.Container, config *dockercontainer.Config) { + effectiveSC := securitycontext.DetermineEffectiveSecurityContext(pod, container) + if effectiveSC == nil { + return + } + if effectiveSC.RunAsUser != nil { + config.User = strconv.Itoa(int(*effectiveSC.RunAsUser)) + } +} + +// ModifyHostConfig is called before the Docker runContainer call. The +// security context provider can make changes to the HostConfig, affecting +// security options, whether the container is privileged, volume binds, etc. +func (p SimpleSecurityContextProvider) ModifyHostConfig(pod *v1.Pod, container *v1.Container, hostConfig *dockercontainer.HostConfig, supplementalGids []int64) { + // Apply supplemental groups + if container.Name != leaky.PodInfraContainerName { + // TODO: We skip application of supplemental groups to the + // infra container to work around a runc issue which + // requires containers to have the '/etc/group'. For + // more information see: + // https://github.com/opencontainers/runc/pull/313 + // This can be removed once the fix makes it into the + // required version of docker. + if pod.Spec.SecurityContext != nil { + for _, group := range pod.Spec.SecurityContext.SupplementalGroups { + hostConfig.GroupAdd = append(hostConfig.GroupAdd, strconv.Itoa(int(group))) + } + if pod.Spec.SecurityContext.FSGroup != nil { + hostConfig.GroupAdd = append(hostConfig.GroupAdd, strconv.Itoa(int(*pod.Spec.SecurityContext.FSGroup))) + } + } + + for _, group := range supplementalGids { + hostConfig.GroupAdd = append(hostConfig.GroupAdd, strconv.Itoa(int(group))) + } + } + + // Apply effective security context for container + effectiveSC := securitycontext.DetermineEffectiveSecurityContext(pod, container) + if effectiveSC == nil { + return + } + + if effectiveSC.Privileged != nil { + hostConfig.Privileged = *effectiveSC.Privileged + } + + if effectiveSC.Capabilities != nil { + add, drop := kubecontainer.MakeCapabilities(effectiveSC.Capabilities.Add, effectiveSC.Capabilities.Drop) + hostConfig.CapAdd = add + hostConfig.CapDrop = drop + } + + if effectiveSC.SELinuxOptions != nil { + hostConfig.SecurityOpt = ModifySecurityOptions(hostConfig.SecurityOpt, effectiveSC.SELinuxOptions) + } +} + +// ModifySecurityOptions adds SELinux options to config. +func ModifySecurityOptions(config []string, selinuxOpts *v1.SELinuxOptions) []string { + config = modifySecurityOption(config, DockerLabelUser, selinuxOpts.User) + config = modifySecurityOption(config, DockerLabelRole, selinuxOpts.Role) + config = modifySecurityOption(config, DockerLabelType, selinuxOpts.Type) + config = modifySecurityOption(config, DockerLabelLevel, selinuxOpts.Level) + + return config +} + +// modifySecurityOption adds the security option of name to the config array with value in the form +// of name:value +func modifySecurityOption(config []string, name, value string) []string { + if len(value) > 0 { + config = append(config, fmt.Sprintf("%s:%s", name, value)) + } + return config +} diff --git a/pkg/securitycontext/provider_test.go b/pkg/kubelet/dockertools/securitycontext/provider_test.go similarity index 100% rename from pkg/securitycontext/provider_test.go rename to pkg/kubelet/dockertools/securitycontext/provider_test.go diff --git a/pkg/securitycontext/types.go b/pkg/kubelet/dockertools/securitycontext/types.go similarity index 100% rename from pkg/securitycontext/types.go rename to pkg/kubelet/dockertools/securitycontext/types.go diff --git a/pkg/kubelet/rkt/rkt.go b/pkg/kubelet/rkt/rkt.go index 36e4ffcb2b..efd40beee6 100644 --- a/pkg/kubelet/rkt/rkt.go +++ b/pkg/kubelet/rkt/rkt.go @@ -347,7 +347,7 @@ func setIsolators(app *appctypes.App, c *v1.Container, ctx *v1.SecurityContext) var addCaps, dropCaps []string if ctx.Capabilities != nil { - addCaps, dropCaps = securitycontext.MakeCapabilities(ctx.Capabilities.Add, ctx.Capabilities.Drop) + addCaps, dropCaps = kubecontainer.MakeCapabilities(ctx.Capabilities.Add, ctx.Capabilities.Drop) } if ctx.Privileged != nil && *ctx.Privileged { addCaps, dropCaps = allCapabilities(), []string{} diff --git a/pkg/securitycontext/BUILD b/pkg/securitycontext/BUILD index 22a2343000..1f461d8307 100644 --- a/pkg/securitycontext/BUILD +++ b/pkg/securitycontext/BUILD @@ -13,32 +13,21 @@ go_library( srcs = [ "doc.go", "fake.go", - "provider.go", - "types.go", "util.go", ], tags = ["automanaged"], deps = [ "//pkg/api:go_default_library", "//pkg/api/v1:go_default_library", - "//pkg/kubelet/leaky:go_default_library", - "//vendor:github.com/docker/engine-api/types/container", ], ) go_test( name = "go_default_test", - srcs = [ - "provider_test.go", - "util_test.go", - ], + srcs = ["util_test.go"], library = ":go_default_library", tags = ["automanaged"], - deps = [ - "//pkg/api/testing:go_default_library", - "//pkg/api/v1:go_default_library", - "//vendor:github.com/docker/engine-api/types/container", - ], + deps = ["//pkg/api/v1:go_default_library"], ) filegroup( diff --git a/pkg/securitycontext/fake.go b/pkg/securitycontext/fake.go index f40d64b75c..1f9dae33dd 100644 --- a/pkg/securitycontext/fake.go +++ b/pkg/securitycontext/fake.go @@ -19,8 +19,6 @@ package securitycontext import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/v1" - - dockercontainer "github.com/docker/engine-api/types/container" ) // ValidSecurityContextWithContainerDefaults creates a valid security context provider based on @@ -33,18 +31,6 @@ func ValidSecurityContextWithContainerDefaults() *v1.SecurityContext { } } -// NewFakeSecurityContextProvider creates a new, no-op security context provider. -func NewFakeSecurityContextProvider() SecurityContextProvider { - return FakeSecurityContextProvider{} -} - -type FakeSecurityContextProvider struct{} - -func (p FakeSecurityContextProvider) ModifyContainerConfig(pod *v1.Pod, container *v1.Container, config *dockercontainer.Config) { -} -func (p FakeSecurityContextProvider) ModifyHostConfig(pod *v1.Pod, container *v1.Container, hostConfig *dockercontainer.HostConfig, supplementalGids []int64) { -} - // ValidInternalSecurityContextWithContainerDefaults creates a valid security context provider based on // empty container defaults. Used for testing. func ValidInternalSecurityContextWithContainerDefaults() *api.SecurityContext { diff --git a/pkg/securitycontext/provider.go b/pkg/securitycontext/provider.go deleted file mode 100644 index 57953d7c33..0000000000 --- a/pkg/securitycontext/provider.go +++ /dev/null @@ -1,274 +0,0 @@ -/* -Copyright 2014 The Kubernetes Authors. - -Licensed 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 securitycontext - -import ( - "fmt" - "strconv" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/v1" - "k8s.io/kubernetes/pkg/kubelet/leaky" - - dockercontainer "github.com/docker/engine-api/types/container" -) - -// NewSimpleSecurityContextProvider creates a new SimpleSecurityContextProvider. -func NewSimpleSecurityContextProvider() SecurityContextProvider { - return SimpleSecurityContextProvider{} -} - -// SimpleSecurityContextProvider is the default implementation of a SecurityContextProvider. -type SimpleSecurityContextProvider struct{} - -// ModifyContainerConfig is called before the Docker createContainer call. -// The security context provider can make changes to the Config with which -// the container is created. -func (p SimpleSecurityContextProvider) ModifyContainerConfig(pod *v1.Pod, container *v1.Container, config *dockercontainer.Config) { - effectiveSC := DetermineEffectiveSecurityContext(pod, container) - if effectiveSC == nil { - return - } - if effectiveSC.RunAsUser != nil { - config.User = strconv.Itoa(int(*effectiveSC.RunAsUser)) - } -} - -// ModifyHostConfig is called before the Docker runContainer call. The -// security context provider can make changes to the HostConfig, affecting -// security options, whether the container is privileged, volume binds, etc. -func (p SimpleSecurityContextProvider) ModifyHostConfig(pod *v1.Pod, container *v1.Container, hostConfig *dockercontainer.HostConfig, supplementalGids []int64) { - // Apply supplemental groups - if container.Name != leaky.PodInfraContainerName { - // TODO: We skip application of supplemental groups to the - // infra container to work around a runc issue which - // requires containers to have the '/etc/group'. For - // more information see: - // https://github.com/opencontainers/runc/pull/313 - // This can be removed once the fix makes it into the - // required version of docker. - if pod.Spec.SecurityContext != nil { - for _, group := range pod.Spec.SecurityContext.SupplementalGroups { - hostConfig.GroupAdd = append(hostConfig.GroupAdd, strconv.Itoa(int(group))) - } - if pod.Spec.SecurityContext.FSGroup != nil { - hostConfig.GroupAdd = append(hostConfig.GroupAdd, strconv.Itoa(int(*pod.Spec.SecurityContext.FSGroup))) - } - } - - for _, group := range supplementalGids { - hostConfig.GroupAdd = append(hostConfig.GroupAdd, strconv.Itoa(int(group))) - } - } - - // Apply effective security context for container - effectiveSC := DetermineEffectiveSecurityContext(pod, container) - if effectiveSC == nil { - return - } - - if effectiveSC.Privileged != nil { - hostConfig.Privileged = *effectiveSC.Privileged - } - - if effectiveSC.Capabilities != nil { - add, drop := MakeCapabilities(effectiveSC.Capabilities.Add, effectiveSC.Capabilities.Drop) - hostConfig.CapAdd = add - hostConfig.CapDrop = drop - } - - if effectiveSC.SELinuxOptions != nil { - hostConfig.SecurityOpt = ModifySecurityOptions(hostConfig.SecurityOpt, effectiveSC.SELinuxOptions) - } -} - -// ModifySecurityOptions adds SELinux options to config. -func ModifySecurityOptions(config []string, selinuxOpts *v1.SELinuxOptions) []string { - config = modifySecurityOption(config, DockerLabelUser, selinuxOpts.User) - config = modifySecurityOption(config, DockerLabelRole, selinuxOpts.Role) - config = modifySecurityOption(config, DockerLabelType, selinuxOpts.Type) - config = modifySecurityOption(config, DockerLabelLevel, selinuxOpts.Level) - - return config -} - -// modifySecurityOption adds the security option of name to the config array with value in the form -// of name:value -func modifySecurityOption(config []string, name, value string) []string { - if len(value) > 0 { - config = append(config, fmt.Sprintf("%s:%s", name, value)) - } - return config -} - -// MakeCapabilities creates string slices from Capability slices -func MakeCapabilities(capAdd []v1.Capability, capDrop []v1.Capability) ([]string, []string) { - var ( - addCaps []string - dropCaps []string - ) - for _, cap := range capAdd { - addCaps = append(addCaps, string(cap)) - } - for _, cap := range capDrop { - dropCaps = append(dropCaps, string(cap)) - } - return addCaps, dropCaps -} - -func DetermineEffectiveSecurityContext(pod *v1.Pod, container *v1.Container) *v1.SecurityContext { - effectiveSc := securityContextFromPodSecurityContext(pod) - containerSc := container.SecurityContext - - if effectiveSc == nil && containerSc == nil { - return nil - } - if effectiveSc != nil && containerSc == nil { - return effectiveSc - } - if effectiveSc == nil && containerSc != nil { - return containerSc - } - - if containerSc.SELinuxOptions != nil { - effectiveSc.SELinuxOptions = new(v1.SELinuxOptions) - *effectiveSc.SELinuxOptions = *containerSc.SELinuxOptions - } - - if containerSc.Capabilities != nil { - effectiveSc.Capabilities = new(v1.Capabilities) - *effectiveSc.Capabilities = *containerSc.Capabilities - } - - if containerSc.Privileged != nil { - effectiveSc.Privileged = new(bool) - *effectiveSc.Privileged = *containerSc.Privileged - } - - if containerSc.RunAsUser != nil { - effectiveSc.RunAsUser = new(int64) - *effectiveSc.RunAsUser = *containerSc.RunAsUser - } - - if containerSc.RunAsNonRoot != nil { - effectiveSc.RunAsNonRoot = new(bool) - *effectiveSc.RunAsNonRoot = *containerSc.RunAsNonRoot - } - - if containerSc.ReadOnlyRootFilesystem != nil { - effectiveSc.ReadOnlyRootFilesystem = new(bool) - *effectiveSc.ReadOnlyRootFilesystem = *containerSc.ReadOnlyRootFilesystem - } - - return effectiveSc -} - -func securityContextFromPodSecurityContext(pod *v1.Pod) *v1.SecurityContext { - if pod.Spec.SecurityContext == nil { - return nil - } - - synthesized := &v1.SecurityContext{} - - if pod.Spec.SecurityContext.SELinuxOptions != nil { - synthesized.SELinuxOptions = &v1.SELinuxOptions{} - *synthesized.SELinuxOptions = *pod.Spec.SecurityContext.SELinuxOptions - } - if pod.Spec.SecurityContext.RunAsUser != nil { - synthesized.RunAsUser = new(int64) - *synthesized.RunAsUser = *pod.Spec.SecurityContext.RunAsUser - } - - if pod.Spec.SecurityContext.RunAsNonRoot != nil { - synthesized.RunAsNonRoot = new(bool) - *synthesized.RunAsNonRoot = *pod.Spec.SecurityContext.RunAsNonRoot - } - - return synthesized -} - -// TODO: remove the duplicate code -func InternalDetermineEffectiveSecurityContext(pod *api.Pod, container *api.Container) *api.SecurityContext { - effectiveSc := internalSecurityContextFromPodSecurityContext(pod) - containerSc := container.SecurityContext - - if effectiveSc == nil && containerSc == nil { - return nil - } - if effectiveSc != nil && containerSc == nil { - return effectiveSc - } - if effectiveSc == nil && containerSc != nil { - return containerSc - } - - if containerSc.SELinuxOptions != nil { - effectiveSc.SELinuxOptions = new(api.SELinuxOptions) - *effectiveSc.SELinuxOptions = *containerSc.SELinuxOptions - } - - if containerSc.Capabilities != nil { - effectiveSc.Capabilities = new(api.Capabilities) - *effectiveSc.Capabilities = *containerSc.Capabilities - } - - if containerSc.Privileged != nil { - effectiveSc.Privileged = new(bool) - *effectiveSc.Privileged = *containerSc.Privileged - } - - if containerSc.RunAsUser != nil { - effectiveSc.RunAsUser = new(int64) - *effectiveSc.RunAsUser = *containerSc.RunAsUser - } - - if containerSc.RunAsNonRoot != nil { - effectiveSc.RunAsNonRoot = new(bool) - *effectiveSc.RunAsNonRoot = *containerSc.RunAsNonRoot - } - - if containerSc.ReadOnlyRootFilesystem != nil { - effectiveSc.ReadOnlyRootFilesystem = new(bool) - *effectiveSc.ReadOnlyRootFilesystem = *containerSc.ReadOnlyRootFilesystem - } - - return effectiveSc -} - -func internalSecurityContextFromPodSecurityContext(pod *api.Pod) *api.SecurityContext { - if pod.Spec.SecurityContext == nil { - return nil - } - - synthesized := &api.SecurityContext{} - - if pod.Spec.SecurityContext.SELinuxOptions != nil { - synthesized.SELinuxOptions = &api.SELinuxOptions{} - *synthesized.SELinuxOptions = *pod.Spec.SecurityContext.SELinuxOptions - } - if pod.Spec.SecurityContext.RunAsUser != nil { - synthesized.RunAsUser = new(int64) - *synthesized.RunAsUser = *pod.Spec.SecurityContext.RunAsUser - } - - if pod.Spec.SecurityContext.RunAsNonRoot != nil { - synthesized.RunAsNonRoot = new(bool) - *synthesized.RunAsNonRoot = *pod.Spec.SecurityContext.RunAsNonRoot - } - - return synthesized -} diff --git a/pkg/securitycontext/util.go b/pkg/securitycontext/util.go index 0d71b1d667..dacc25f202 100644 --- a/pkg/securitycontext/util.go +++ b/pkg/securitycontext/util.go @@ -20,6 +20,7 @@ import ( "fmt" "strings" + "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/v1" ) @@ -87,3 +88,146 @@ func HasRunAsUser(container *v1.Container) bool { func HasRootRunAsUser(container *v1.Container) bool { return HasRunAsUser(container) && HasRootUID(container) } + +func DetermineEffectiveSecurityContext(pod *v1.Pod, container *v1.Container) *v1.SecurityContext { + effectiveSc := securityContextFromPodSecurityContext(pod) + containerSc := container.SecurityContext + + if effectiveSc == nil && containerSc == nil { + return nil + } + if effectiveSc != nil && containerSc == nil { + return effectiveSc + } + if effectiveSc == nil && containerSc != nil { + return containerSc + } + + if containerSc.SELinuxOptions != nil { + effectiveSc.SELinuxOptions = new(v1.SELinuxOptions) + *effectiveSc.SELinuxOptions = *containerSc.SELinuxOptions + } + + if containerSc.Capabilities != nil { + effectiveSc.Capabilities = new(v1.Capabilities) + *effectiveSc.Capabilities = *containerSc.Capabilities + } + + if containerSc.Privileged != nil { + effectiveSc.Privileged = new(bool) + *effectiveSc.Privileged = *containerSc.Privileged + } + + if containerSc.RunAsUser != nil { + effectiveSc.RunAsUser = new(int64) + *effectiveSc.RunAsUser = *containerSc.RunAsUser + } + + if containerSc.RunAsNonRoot != nil { + effectiveSc.RunAsNonRoot = new(bool) + *effectiveSc.RunAsNonRoot = *containerSc.RunAsNonRoot + } + + if containerSc.ReadOnlyRootFilesystem != nil { + effectiveSc.ReadOnlyRootFilesystem = new(bool) + *effectiveSc.ReadOnlyRootFilesystem = *containerSc.ReadOnlyRootFilesystem + } + + return effectiveSc +} + +func securityContextFromPodSecurityContext(pod *v1.Pod) *v1.SecurityContext { + if pod.Spec.SecurityContext == nil { + return nil + } + + synthesized := &v1.SecurityContext{} + + if pod.Spec.SecurityContext.SELinuxOptions != nil { + synthesized.SELinuxOptions = &v1.SELinuxOptions{} + *synthesized.SELinuxOptions = *pod.Spec.SecurityContext.SELinuxOptions + } + if pod.Spec.SecurityContext.RunAsUser != nil { + synthesized.RunAsUser = new(int64) + *synthesized.RunAsUser = *pod.Spec.SecurityContext.RunAsUser + } + + if pod.Spec.SecurityContext.RunAsNonRoot != nil { + synthesized.RunAsNonRoot = new(bool) + *synthesized.RunAsNonRoot = *pod.Spec.SecurityContext.RunAsNonRoot + } + + return synthesized +} + +// TODO: remove the duplicate code +func InternalDetermineEffectiveSecurityContext(pod *api.Pod, container *api.Container) *api.SecurityContext { + effectiveSc := internalSecurityContextFromPodSecurityContext(pod) + containerSc := container.SecurityContext + + if effectiveSc == nil && containerSc == nil { + return nil + } + if effectiveSc != nil && containerSc == nil { + return effectiveSc + } + if effectiveSc == nil && containerSc != nil { + return containerSc + } + + if containerSc.SELinuxOptions != nil { + effectiveSc.SELinuxOptions = new(api.SELinuxOptions) + *effectiveSc.SELinuxOptions = *containerSc.SELinuxOptions + } + + if containerSc.Capabilities != nil { + effectiveSc.Capabilities = new(api.Capabilities) + *effectiveSc.Capabilities = *containerSc.Capabilities + } + + if containerSc.Privileged != nil { + effectiveSc.Privileged = new(bool) + *effectiveSc.Privileged = *containerSc.Privileged + } + + if containerSc.RunAsUser != nil { + effectiveSc.RunAsUser = new(int64) + *effectiveSc.RunAsUser = *containerSc.RunAsUser + } + + if containerSc.RunAsNonRoot != nil { + effectiveSc.RunAsNonRoot = new(bool) + *effectiveSc.RunAsNonRoot = *containerSc.RunAsNonRoot + } + + if containerSc.ReadOnlyRootFilesystem != nil { + effectiveSc.ReadOnlyRootFilesystem = new(bool) + *effectiveSc.ReadOnlyRootFilesystem = *containerSc.ReadOnlyRootFilesystem + } + + return effectiveSc +} + +func internalSecurityContextFromPodSecurityContext(pod *api.Pod) *api.SecurityContext { + if pod.Spec.SecurityContext == nil { + return nil + } + + synthesized := &api.SecurityContext{} + + if pod.Spec.SecurityContext.SELinuxOptions != nil { + synthesized.SELinuxOptions = &api.SELinuxOptions{} + *synthesized.SELinuxOptions = *pod.Spec.SecurityContext.SELinuxOptions + } + if pod.Spec.SecurityContext.RunAsUser != nil { + synthesized.RunAsUser = new(int64) + *synthesized.RunAsUser = *pod.Spec.SecurityContext.RunAsUser + } + + if pod.Spec.SecurityContext.RunAsNonRoot != nil { + synthesized.RunAsNonRoot = new(bool) + *synthesized.RunAsNonRoot = *pod.Spec.SecurityContext.RunAsNonRoot + } + + return synthesized +}