diff --git a/pkg/kubelet/dockershim/convert.go b/pkg/kubelet/dockershim/convert.go index 7e350f5596a..25e5851ae6c 100644 --- a/pkg/kubelet/dockershim/convert.go +++ b/pkg/kubelet/dockershim/convert.go @@ -18,12 +18,14 @@ package dockershim import ( "fmt" + "strconv" "strings" "time" dockertypes "github.com/docker/engine-api/types" runtimeApi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime" + "k8s.io/kubernetes/pkg/kubelet/dockertools" ) // This file contains helper functions to convert docker API types to runtime @@ -55,14 +57,25 @@ func imageInspectToRuntimeAPIImage(image *dockertypes.ImageInspect) (*runtimeApi return nil, fmt.Errorf("unable to convert a nil pointer to a runtime API image") } + var err error + var uid int64 size := uint64(image.VirtualSize) + imageUid := dockertools.GetUidFromUser(image.Config.User) + // Convert image UID to int64 format. Not that it assumes the process in + // the image is running as root if image.Config.User is not set. + if imageUid != "" { + uid, err = strconv.ParseInt(imageUid, 10, 64) + if err != nil { + return nil, fmt.Errorf("non-numeric user (%q)", imageUid) + } + } return &runtimeApi.Image{ Id: &image.ID, RepoTags: image.RepoTags, RepoDigests: image.RepoDigests, Size_: &size, + Uid: &uid, }, nil - } func toPullableImageID(id string, image *dockertypes.ImageInspect) string { diff --git a/pkg/kubelet/dockershim/docker_container.go b/pkg/kubelet/dockershim/docker_container.go index a62aa152c30..ad8438184bb 100644 --- a/pkg/kubelet/dockershim/docker_container.go +++ b/pkg/kubelet/dockershim/docker_container.go @@ -120,34 +120,15 @@ func (ds *dockerService) CreateContainer(podSandboxID string, config *runtimeApi // Fill the HostConfig. hc := &dockercontainer.HostConfig{ - Binds: generateMountBindings(config.GetMounts()), - ReadonlyRootfs: config.GetReadonlyRootfs(), - Privileged: config.GetPrivileged(), + Binds: generateMountBindings(config.GetMounts()), } - // Apply options derived from the sandbox config. + // Apply cgroupsParent derived from the sandbox config. if lc := sandboxConfig.GetLinux(); lc != nil { // Apply Cgroup options. // TODO: Check if this works with per-pod cgroups. // TODO: we need to pass the cgroup in syntax expected by cgroup driver but shim does not use docker info yet... hc.CgroupParent = lc.GetCgroupParent() - - // Apply namespace options. - sandboxNSMode := fmt.Sprintf("container:%v", podSandboxID) - hc.NetworkMode = dockercontainer.NetworkMode(sandboxNSMode) - hc.IpcMode = dockercontainer.IpcMode(sandboxNSMode) - hc.UTSMode = "" - hc.PidMode = "" - - nsOpts := lc.GetNamespaceOptions() - if nsOpts != nil { - if nsOpts.GetHostNetwork() { - hc.UTSMode = namespaceModeHost - } - if nsOpts.GetHostPid() { - hc.PidMode = namespaceModeHost - } - } } // Apply Linux-specific options if applicable. @@ -167,6 +148,9 @@ func (ds *dockerService) CreateContainer(podSandboxID string, config *runtimeApi hc.OomScoreAdj = int(rOpts.GetOomScoreAdj()) } // Note: ShmSize is handled in kube_docker_client.go + + // Apply security context. + applyContainerSecurityContext(lc, podSandboxID, createConfig.Config, hc) } // Set devices for container. @@ -180,12 +164,12 @@ func (ds *dockerService) CreateContainer(podSandboxID string, config *runtimeApi } hc.Resources.Devices = devices - var err error - hc.SecurityOpt, err = getContainerSecurityOpts(config.Metadata.GetName(), sandboxConfig, ds.seccompProfileRoot) + // Apply appArmor and seccomp options. + securityOpts, err := getContainerSecurityOpts(config.Metadata.GetName(), sandboxConfig, ds.seccompProfileRoot) if err != nil { return "", fmt.Errorf("failed to generate container security options for container %q: %v", config.Metadata.GetName(), err) } - // TODO: Add or drop capabilities. + hc.SecurityOpt = append(hc.SecurityOpt, securityOpts...) createConfig.HostConfig = hc createResp, err := ds.client.CreateContainer(createConfig) diff --git a/pkg/kubelet/dockershim/docker_sandbox.go b/pkg/kubelet/dockershim/docker_sandbox.go index 171aa51987e..fe9fa142e35 100644 --- a/pkg/kubelet/dockershim/docker_sandbox.go +++ b/pkg/kubelet/dockershim/docker_sandbox.go @@ -79,7 +79,7 @@ func (ds *dockerService) RunPodSandbox(config *runtimeApi.PodSandboxConfig) (str if err != nil { return createResp.ID, fmt.Errorf("failed to start sandbox container for pod %q: %v", config.Metadata.GetName(), err) } - if config.GetLinux().GetNamespaceOptions().GetHostNetwork() { + if config.GetLinux().GetSecurityContext().GetNamespaceOptions().GetHostNetwork() { return createResp.ID, nil } @@ -286,6 +286,18 @@ func (ds *dockerService) ListPodSandbox(filter *runtimeApi.PodSandboxFilter) ([] return result, nil } +// applySandboxLinuxOptions applies LinuxPodSandboxConfig to dockercontainer.HostConfig and dockercontainer.ContainerCreateConfig. +func (ds *dockerService) applySandboxLinuxOptions(hc *dockercontainer.HostConfig, lc *runtimeApi.LinuxPodSandboxConfig, createConfig *dockertypes.ContainerCreateConfig, image string) error { + // Apply Cgroup options. + // TODO: Check if this works with per-pod cgroups. + hc.CgroupParent = lc.GetCgroupParent() + // Apply security context. + applySandboxSecurityContext(lc, createConfig.Config, hc) + + return nil +} + +// makeSandboxDockerConfig returns dockertypes.ContainerCreateConfig based on runtimeApi.PodSandboxConfig. func (ds *dockerService) makeSandboxDockerConfig(c *runtimeApi.PodSandboxConfig, image string) (*dockertypes.ContainerCreateConfig, error) { // Merge annotations and labels because docker supports only labels. labels := makeLabels(c.GetLabels(), c.GetAnnotations()) @@ -316,29 +328,11 @@ func (ds *dockerService) makeSandboxDockerConfig(c *runtimeApi.PodSandboxConfig, // Apply linux-specific options. if lc := c.GetLinux(); lc != nil { - // Apply Cgroup options. - // TODO: Check if this works with per-pod cgroups. - hc.CgroupParent = lc.GetCgroupParent() - - // Apply namespace options. - hc.NetworkMode, hc.UTSMode, hc.PidMode = "", "", "" - nsOpts := lc.GetNamespaceOptions() - if nsOpts != nil { - if nsOpts.GetHostNetwork() { - hc.NetworkMode = namespaceModeHost - } else { - // Assume kubelet uses either the cni or the kubenet plugin. - // TODO: support docker networking. - hc.NetworkMode = "none" - } - if nsOpts.GetHostIpc() { - hc.IpcMode = namespaceModeHost - } - if nsOpts.GetHostPid() { - hc.PidMode = namespaceModeHost - } + if err := ds.applySandboxLinuxOptions(hc, lc, createConfig, image); err != nil { + return nil, err } } + // Set port mappings. exposedPorts, portBindings := makePortsAndBindings(c.GetPortMappings()) createConfig.Config.ExposedPorts = exposedPorts @@ -355,10 +349,11 @@ func (ds *dockerService) makeSandboxDockerConfig(c *runtimeApi.PodSandboxConfig, setSandboxResources(hc) // Set security options. - hc.SecurityOpt, err = getSandboxSecurityOpts(c, ds.seccompProfileRoot) + securityOpts, err := getSandboxSecurityOpts(c, ds.seccompProfileRoot) if err != nil { return nil, fmt.Errorf("failed to generate sandbox security options for sandbox %q: %v", c.Metadata.GetName(), err) } + hc.SecurityOpt = append(hc.SecurityOpt, securityOpts...) return createConfig, nil } diff --git a/pkg/kubelet/dockershim/docker_sandbox_test.go b/pkg/kubelet/dockershim/docker_sandbox_test.go index bf49c25628a..329690b1ca9 100644 --- a/pkg/kubelet/dockershim/docker_sandbox_test.go +++ b/pkg/kubelet/dockershim/docker_sandbox_test.go @@ -186,7 +186,13 @@ func TestHostNetworkPluginInvocation(t *testing.T) { map[string]string{"annotation": ns}, ) hostNetwork := true - c.Linux = &runtimeApi.LinuxPodSandboxConfig{NamespaceOptions: &runtimeApi.NamespaceOption{HostNetwork: &hostNetwork}} + c.Linux = &runtimeApi.LinuxPodSandboxConfig{ + SecurityContext: &runtimeApi.LinuxSandboxSecurityContext{ + NamespaceOptions: &runtimeApi.NamespaceOption{ + HostNetwork: &hostNetwork, + }, + }, + } cID := kubecontainer.ContainerID{Type: runtimeName, ID: fmt.Sprintf("/%v", makeSandboxName(c))} // No calls to network plugin are expected diff --git a/pkg/kubelet/dockershim/helpers.go b/pkg/kubelet/dockershim/helpers.go index 0b67c650625..111388f97ba 100644 --- a/pkg/kubelet/dockershim/helpers.go +++ b/pkg/kubelet/dockershim/helpers.go @@ -123,13 +123,18 @@ func extractLabels(input map[string]string) (map[string]string, map[string]strin // '::Z', if the volume requires SELinux // relabeling and the pod provides an SELinux label func generateMountBindings(mounts []*runtimeApi.Mount) (result []string) { - // TODO: resolve podHasSELinuxLabel for _, m := range mounts { bind := fmt.Sprintf("%s:%s", m.GetHostPath(), m.GetContainerPath()) readOnly := m.GetReadonly() if readOnly { bind += ":ro" } + // Only request relabeling if the pod provides an SELinux context. If the pod + // does not provide an SELinux context relabeling will label the volume with + // the container's randomly allocated MCS label. This would restrict access + // to the volume to the container which mounts it first. + // TODO: always relabel if SELinux is enabled and the volume support relabeling + // (refer #33951 and #33663). if m.GetSelinuxRelabel() { if readOnly { bind += ",Z" diff --git a/pkg/kubelet/dockershim/security_context.go b/pkg/kubelet/dockershim/security_context.go new file mode 100644 index 00000000000..db56d149ba4 --- /dev/null +++ b/pkg/kubelet/dockershim/security_context.go @@ -0,0 +1,158 @@ +/* +Copyright 2016 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 dockershim + +import ( + "fmt" + "strconv" + + dockercontainer "github.com/docker/engine-api/types/container" + + "k8s.io/kubernetes/pkg/api" + runtimeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime" + "k8s.io/kubernetes/pkg/securitycontext" +) + +// applySandboxSecurityContext updates docker sandbox options according to security context. +func applySandboxSecurityContext(lc *runtimeapi.LinuxPodSandboxConfig, config *dockercontainer.Config, hc *dockercontainer.HostConfig) { + if lc == nil { + return + } + + var sc *runtimeapi.LinuxContainerSecurityContext + if lc.SecurityContext != nil { + sc = &runtimeapi.LinuxContainerSecurityContext{ + // TODO: We skip application of supplemental groups to the + // sandbox 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. + RunAsUser: lc.SecurityContext.RunAsUser, + ReadonlyRootfs: lc.SecurityContext.ReadonlyRootfs, + SelinuxOptions: lc.SecurityContext.SelinuxOptions, + NamespaceOptions: lc.SecurityContext.NamespaceOptions, + } + } + + modifyContainerConfig(sc, config) + modifyHostConfig(sc, "", hc) +} + +// applyContainerSecurityContext updates docker container options according to security context. +func applyContainerSecurityContext(lc *runtimeapi.LinuxContainerConfig, sandboxID string, config *dockercontainer.Config, hc *dockercontainer.HostConfig) { + if lc == nil { + return + } + + modifyContainerConfig(lc.SecurityContext, config) + modifyHostConfig(lc.SecurityContext, sandboxID, hc) + return +} + +// modifyContainerConfig applies container security context config to dockercontainer.Config. +func modifyContainerConfig(sc *runtimeapi.LinuxContainerSecurityContext, config *dockercontainer.Config) { + if sc != nil && sc.RunAsUser != nil { + config.User = strconv.FormatInt(sc.GetRunAsUser(), 10) + } +} + +// modifyHostConfig applies security context config to dockercontainer.HostConfig. +func modifyHostConfig(sc *runtimeapi.LinuxContainerSecurityContext, sandboxID string, hostConfig *dockercontainer.HostConfig) { + // Apply namespace options. + modifyNamespaceOptions(sc.GetNamespaceOptions(), sandboxID, hostConfig) + + if sc == nil { + return + } + + // Apply supplemental groups. + for _, group := range sc.SupplementalGroups { + hostConfig.GroupAdd = append(hostConfig.GroupAdd, strconv.FormatInt(group, 10)) + } + + // Apply security context for the container. + if sc.Privileged != nil { + hostConfig.Privileged = sc.GetPrivileged() + } + if sc.ReadonlyRootfs != nil { + hostConfig.ReadonlyRootfs = sc.GetReadonlyRootfs() + } + if sc.Capabilities != nil { + hostConfig.CapAdd = sc.GetCapabilities().GetAddCapabilities() + hostConfig.CapDrop = sc.GetCapabilities().GetDropCapabilities() + } + if sc.SelinuxOptions != nil { + hostConfig.SecurityOpt = securitycontext.ModifySecurityOptions( + hostConfig.SecurityOpt, + &api.SELinuxOptions{ + User: sc.SelinuxOptions.GetUser(), + Role: sc.SelinuxOptions.GetRole(), + Type: sc.SelinuxOptions.GetType(), + Level: sc.SelinuxOptions.GetLevel(), + }, + ) + } +} + +// modifyNamespaceOptions applies namespaceoptions to dockercontainer.HostConfig. +func modifyNamespaceOptions(nsOpts *runtimeapi.NamespaceOption, sandboxID string, hostConfig *dockercontainer.HostConfig) { + hostNetwork := false + if nsOpts != nil { + if nsOpts.HostNetwork != nil { + hostNetwork = nsOpts.GetHostNetwork() + } + if nsOpts.GetHostPid() { + hostConfig.PidMode = namespaceModeHost + } + if nsOpts.GetHostIpc() { + hostConfig.IpcMode = namespaceModeHost + } + } + + // Set for sandbox if sandboxID is not provided. + if sandboxID == "" { + modifyHostNetworkOptionForSandbox(hostNetwork, hostConfig) + } else { + // Set for container is sandboxID is provided. + modifyHostNetworkOptionForContainer(hostNetwork, sandboxID, hostConfig) + } +} + +// modifyHostNetworkOptionForSandbox applies NetworkMode/UTSMode to sandbox's dockercontainer.HostConfig. +func modifyHostNetworkOptionForSandbox(hostNetwork bool, hc *dockercontainer.HostConfig) { + if hostNetwork { + hc.NetworkMode = namespaceModeHost + } else { + // Assume kubelet uses either the cni or the kubenet plugin. + // TODO: support docker networking. + hc.NetworkMode = "none" + } +} + +// modifyHostNetworkOptionForContainer applies NetworkMode/UTSMode to container's dockercontainer.HostConfig. +func modifyHostNetworkOptionForContainer(hostNetwork bool, sandboxID string, hc *dockercontainer.HostConfig) { + sandboxNSMode := fmt.Sprintf("container:%v", sandboxID) + hc.NetworkMode = dockercontainer.NetworkMode(sandboxNSMode) + hc.IpcMode = dockercontainer.IpcMode(sandboxNSMode) + hc.UTSMode = "" + hc.PidMode = "" + + if hostNetwork { + hc.UTSMode = namespaceModeHost + } +} diff --git a/pkg/kubelet/dockershim/security_context_test.go b/pkg/kubelet/dockershim/security_context_test.go new file mode 100644 index 00000000000..b22dc396b0f --- /dev/null +++ b/pkg/kubelet/dockershim/security_context_test.go @@ -0,0 +1,257 @@ +/* +Copyright 2016 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 dockershim + +import ( + "fmt" + "strconv" + "testing" + + dockercontainer "github.com/docker/engine-api/types/container" + "github.com/stretchr/testify/assert" + + runtimeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime" + "k8s.io/kubernetes/pkg/securitycontext" +) + +func TestModifyContainerConfig(t *testing.T) { + var uid int64 = 123 + + cases := []struct { + name string + sc *runtimeapi.LinuxContainerSecurityContext + expected *dockercontainer.Config + }{ + { + name: "container.SecurityContext.RunAsUser set", + sc: &runtimeapi.LinuxContainerSecurityContext{ + RunAsUser: &uid, + }, + expected: &dockercontainer.Config{ + User: strconv.FormatInt(uid, 10), + }, + }, + { + name: "no RunAsUser value set", + sc: &runtimeapi.LinuxContainerSecurityContext{}, + expected: &dockercontainer.Config{}, + }, + } + + for _, tc := range cases { + dockerCfg := &dockercontainer.Config{} + modifyContainerConfig(tc.sc, dockerCfg) + assert.Equal(t, tc.expected, dockerCfg, "[Test case %q]", tc.name) + } +} + +func TestModifyHostConfig(t *testing.T) { + priv := true + setPrivSC := &runtimeapi.LinuxContainerSecurityContext{} + setPrivSC.Privileged = &priv + setPrivHC := &dockercontainer.HostConfig{ + Privileged: true, + NetworkMode: "none", + } + setCapsHC := &dockercontainer.HostConfig{ + NetworkMode: "none", + CapAdd: []string{"addCapA", "addCapB"}, + CapDrop: []string{"dropCapA", "dropCapB"}, + } + setSELinuxHC := &dockercontainer.HostConfig{ + NetworkMode: "none", + SecurityOpt: []string{ + fmt.Sprintf("%s:%s", securitycontext.DockerLabelUser, "user"), + fmt.Sprintf("%s:%s", securitycontext.DockerLabelRole, "role"), + fmt.Sprintf("%s:%s", securitycontext.DockerLabelType, "type"), + fmt.Sprintf("%s:%s", securitycontext.DockerLabelLevel, "level"), + }, + } + + cases := []struct { + name string + sc *runtimeapi.LinuxContainerSecurityContext + expected *dockercontainer.HostConfig + }{ + { + name: "fully set container.SecurityContext", + sc: fullValidSecurityContext(), + expected: fullValidHostConfig(), + }, + { + name: "container.SecurityContext.Privileged", + sc: setPrivSC, + expected: setPrivHC, + }, + { + name: "container.SecurityContext.Capabilities", + sc: &runtimeapi.LinuxContainerSecurityContext{ + Capabilities: inputCapabilities(), + }, + expected: setCapsHC, + }, + { + name: "container.SecurityContext.SELinuxOptions", + sc: &runtimeapi.LinuxContainerSecurityContext{ + SelinuxOptions: inputSELinuxOptions(), + }, + expected: setSELinuxHC, + }, + } + + for _, tc := range cases { + dockerCfg := &dockercontainer.HostConfig{} + modifyHostConfig(tc.sc, "", dockerCfg) + assert.Equal(t, tc.expected, dockerCfg, "[Test case %q]", tc.name) + } +} + +func TestModifyHostConfigWithGroups(t *testing.T) { + supplementalGroupsSC := &runtimeapi.LinuxContainerSecurityContext{} + supplementalGroupsSC.SupplementalGroups = []int64{2222} + supplementalGroupHC := &dockercontainer.HostConfig{NetworkMode: "none"} + supplementalGroupHC.GroupAdd = []string{"2222"} + + testCases := []struct { + name string + securityContext *runtimeapi.LinuxContainerSecurityContext + expected *dockercontainer.HostConfig + }{ + { + name: "nil", + securityContext: nil, + expected: &dockercontainer.HostConfig{NetworkMode: "none"}, + }, + { + name: "SupplementalGroup", + securityContext: supplementalGroupsSC, + expected: supplementalGroupHC, + }, + } + + for _, tc := range testCases { + dockerCfg := &dockercontainer.HostConfig{} + modifyHostConfig(tc.securityContext, "", dockerCfg) + assert.Equal(t, tc.expected, dockerCfg, "[Test case %q]", tc.name) + } +} + +func TestModifyHostConfigWithSandboxID(t *testing.T) { + priv := true + sandboxID := "sandbox" + sandboxNSMode := fmt.Sprintf("container:%v", sandboxID) + setPrivSC := &runtimeapi.LinuxContainerSecurityContext{} + setPrivSC.Privileged = &priv + setPrivHC := &dockercontainer.HostConfig{ + Privileged: true, + IpcMode: dockercontainer.IpcMode(sandboxNSMode), + NetworkMode: dockercontainer.NetworkMode(sandboxNSMode), + } + setCapsHC := &dockercontainer.HostConfig{ + CapAdd: []string{"addCapA", "addCapB"}, + CapDrop: []string{"dropCapA", "dropCapB"}, + IpcMode: dockercontainer.IpcMode(sandboxNSMode), + NetworkMode: dockercontainer.NetworkMode(sandboxNSMode), + } + setSELinuxHC := &dockercontainer.HostConfig{ + SecurityOpt: []string{ + fmt.Sprintf("%s:%s", securitycontext.DockerLabelUser, "user"), + fmt.Sprintf("%s:%s", securitycontext.DockerLabelRole, "role"), + fmt.Sprintf("%s:%s", securitycontext.DockerLabelType, "type"), + fmt.Sprintf("%s:%s", securitycontext.DockerLabelLevel, "level"), + }, + IpcMode: dockercontainer.IpcMode(sandboxNSMode), + NetworkMode: dockercontainer.NetworkMode(sandboxNSMode), + } + + cases := []struct { + name string + sc *runtimeapi.LinuxContainerSecurityContext + expected *dockercontainer.HostConfig + }{ + { + name: "container.SecurityContext.Privileged", + sc: setPrivSC, + expected: setPrivHC, + }, + { + name: "container.SecurityContext.Capabilities", + sc: &runtimeapi.LinuxContainerSecurityContext{ + Capabilities: inputCapabilities(), + }, + expected: setCapsHC, + }, + { + name: "container.SecurityContext.SELinuxOptions", + sc: &runtimeapi.LinuxContainerSecurityContext{ + SelinuxOptions: inputSELinuxOptions(), + }, + expected: setSELinuxHC, + }, + } + + for _, tc := range cases { + dockerCfg := &dockercontainer.HostConfig{} + modifyHostConfig(tc.sc, sandboxID, dockerCfg) + assert.Equal(t, tc.expected, dockerCfg, "[Test case %q]", tc.name) + } +} + +func fullValidSecurityContext() *runtimeapi.LinuxContainerSecurityContext { + priv := true + return &runtimeapi.LinuxContainerSecurityContext{ + Privileged: &priv, + Capabilities: inputCapabilities(), + SelinuxOptions: inputSELinuxOptions(), + } +} + +func inputCapabilities() *runtimeapi.Capability { + return &runtimeapi.Capability{ + AddCapabilities: []string{"addCapA", "addCapB"}, + DropCapabilities: []string{"dropCapA", "dropCapB"}, + } +} + +func inputSELinuxOptions() *runtimeapi.SELinuxOption { + user := "user" + role := "role" + stype := "type" + level := "level" + + return &runtimeapi.SELinuxOption{ + User: &user, + Role: &role, + Type: &stype, + Level: &level, + } +} + +func fullValidHostConfig() *dockercontainer.HostConfig { + return &dockercontainer.HostConfig{ + Privileged: true, + NetworkMode: "none", + CapAdd: []string{"addCapA", "addCapB"}, + CapDrop: []string{"dropCapA", "dropCapB"}, + SecurityOpt: []string{ + fmt.Sprintf("%s:%s", securitycontext.DockerLabelUser, "user"), + fmt.Sprintf("%s:%s", securitycontext.DockerLabelRole, "role"), + fmt.Sprintf("%s:%s", securitycontext.DockerLabelType, "type"), + fmt.Sprintf("%s:%s", securitycontext.DockerLabelLevel, "level"), + }, + } +} diff --git a/pkg/kubelet/dockertools/docker_manager.go b/pkg/kubelet/dockertools/docker_manager.go index 55a5ba1ae7c..e34debc1fe5 100644 --- a/pkg/kubelet/dockertools/docker_manager.go +++ b/pkg/kubelet/dockertools/docker_manager.go @@ -2474,7 +2474,7 @@ func (dm *DockerManager) isImageRoot(image string) (bool, error) { return false, fmt.Errorf("unable to inspect image %s, nil Config", image) } - user := getUidFromUser(img.Config.User) + user := GetUidFromUser(img.Config.User) // if no user is defined container will run as root if user == "" { return true, nil @@ -2488,8 +2488,8 @@ func (dm *DockerManager) isImageRoot(image string) (bool, error) { return uid == 0, nil } -// getUidFromUser splits the uid out of an uid:gid string. -func getUidFromUser(id string) string { +// GetUidFromUser splits the uid out of an uid:gid string. +func GetUidFromUser(id string) string { if id == "" { return id } diff --git a/pkg/kubelet/dockertools/docker_manager_test.go b/pkg/kubelet/dockertools/docker_manager_test.go index a350def7bff..592c09673e9 100644 --- a/pkg/kubelet/dockertools/docker_manager_test.go +++ b/pkg/kubelet/dockertools/docker_manager_test.go @@ -1454,7 +1454,7 @@ func TestGetUidFromUser(t *testing.T) { }, } for k, v := range tests { - actual := getUidFromUser(v.input) + actual := GetUidFromUser(v.input) if actual != v.expect { t.Errorf("%s failed. Expected %s but got %s", k, v.expect, actual) } diff --git a/pkg/securitycontext/provider.go b/pkg/securitycontext/provider.go index 524aa772353..1b17a2abc0c 100644 --- a/pkg/securitycontext/provider.go +++ b/pkg/securitycontext/provider.go @@ -91,13 +91,20 @@ func (p SimpleSecurityContextProvider) ModifyHostConfig(pod *api.Pod, container } if effectiveSC.SELinuxOptions != nil { - hostConfig.SecurityOpt = modifySecurityOption(hostConfig.SecurityOpt, dockerLabelUser, effectiveSC.SELinuxOptions.User) - hostConfig.SecurityOpt = modifySecurityOption(hostConfig.SecurityOpt, dockerLabelRole, effectiveSC.SELinuxOptions.Role) - hostConfig.SecurityOpt = modifySecurityOption(hostConfig.SecurityOpt, dockerLabelType, effectiveSC.SELinuxOptions.Type) - hostConfig.SecurityOpt = modifySecurityOption(hostConfig.SecurityOpt, dockerLabelLevel, effectiveSC.SELinuxOptions.Level) + hostConfig.SecurityOpt = ModifySecurityOptions(hostConfig.SecurityOpt, effectiveSC.SELinuxOptions) } } +// ModifySecurityOptions adds SELinux options to config. +func ModifySecurityOptions(config []string, selinuxOpts *api.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 { diff --git a/pkg/securitycontext/provider_test.go b/pkg/securitycontext/provider_test.go index 37012039b51..0c955647ba4 100644 --- a/pkg/securitycontext/provider_test.go +++ b/pkg/securitycontext/provider_test.go @@ -104,10 +104,10 @@ func TestModifyHostConfig(t *testing.T) { setSELinuxHC := &dockercontainer.HostConfig{} setSELinuxHC.SecurityOpt = []string{ - fmt.Sprintf("%s:%s", dockerLabelUser, "user"), - fmt.Sprintf("%s:%s", dockerLabelRole, "role"), - fmt.Sprintf("%s:%s", dockerLabelType, "type"), - fmt.Sprintf("%s:%s", dockerLabelLevel, "level"), + fmt.Sprintf("%s:%s", DockerLabelUser, "user"), + fmt.Sprintf("%s:%s", DockerLabelRole, "role"), + fmt.Sprintf("%s:%s", DockerLabelType, "type"), + fmt.Sprintf("%s:%s", DockerLabelLevel, "level"), } // seLinuxLabelsSC := fullValidSecurityContext() @@ -325,10 +325,10 @@ func fullValidHostConfig() *dockercontainer.HostConfig { CapAdd: []string{"addCapA", "addCapB"}, CapDrop: []string{"dropCapA", "dropCapB"}, SecurityOpt: []string{ - fmt.Sprintf("%s:%s", dockerLabelUser, "user"), - fmt.Sprintf("%s:%s", dockerLabelRole, "role"), - fmt.Sprintf("%s:%s", dockerLabelType, "type"), - fmt.Sprintf("%s:%s", dockerLabelLevel, "level"), + fmt.Sprintf("%s:%s", DockerLabelUser, "user"), + fmt.Sprintf("%s:%s", DockerLabelRole, "role"), + fmt.Sprintf("%s:%s", DockerLabelType, "type"), + fmt.Sprintf("%s:%s", DockerLabelLevel, "level"), }, } } diff --git a/pkg/securitycontext/types.go b/pkg/securitycontext/types.go index 2ab2d0390b5..500c2ba2b64 100644 --- a/pkg/securitycontext/types.go +++ b/pkg/securitycontext/types.go @@ -41,9 +41,9 @@ type SecurityContextProvider interface { } const ( - dockerLabelUser string = "label:user" - dockerLabelRole string = "label:role" - dockerLabelType string = "label:type" - dockerLabelLevel string = "label:level" - dockerLabelDisable string = "label:disable" + DockerLabelUser string = "label:user" + DockerLabelRole string = "label:role" + DockerLabelType string = "label:type" + DockerLabelLevel string = "label:level" + DockerLabelDisable string = "label:disable" )