mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-31 15:25:57 +00:00
Add security context support in dockershim
This commit is contained in:
parent
3df60eb163
commit
3aee57d4ae
@ -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 {
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -123,13 +123,18 @@ func extractLabels(input map[string]string) (map[string]string, map[string]strin
|
||||
// '<HostPath>:<ContainerPath>: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"
|
||||
|
158
pkg/kubelet/dockershim/security_context.go
Normal file
158
pkg/kubelet/dockershim/security_context.go
Normal file
@ -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
|
||||
}
|
||||
}
|
257
pkg/kubelet/dockershim/security_context_test.go
Normal file
257
pkg/kubelet/dockershim/security_context_test.go
Normal file
@ -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"),
|
||||
},
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -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"
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user