mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-06 10:43:56 +00:00
Add security context support in dockershim
This commit is contained in:
parent
3df60eb163
commit
3aee57d4ae
@ -18,12 +18,14 @@ package dockershim
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
dockertypes "github.com/docker/engine-api/types"
|
dockertypes "github.com/docker/engine-api/types"
|
||||||
|
|
||||||
runtimeApi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
|
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
|
// 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")
|
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)
|
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{
|
return &runtimeApi.Image{
|
||||||
Id: &image.ID,
|
Id: &image.ID,
|
||||||
RepoTags: image.RepoTags,
|
RepoTags: image.RepoTags,
|
||||||
RepoDigests: image.RepoDigests,
|
RepoDigests: image.RepoDigests,
|
||||||
Size_: &size,
|
Size_: &size,
|
||||||
|
Uid: &uid,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func toPullableImageID(id string, image *dockertypes.ImageInspect) string {
|
func toPullableImageID(id string, image *dockertypes.ImageInspect) string {
|
||||||
|
@ -121,33 +121,14 @@ func (ds *dockerService) CreateContainer(podSandboxID string, config *runtimeApi
|
|||||||
// Fill the HostConfig.
|
// Fill the HostConfig.
|
||||||
hc := &dockercontainer.HostConfig{
|
hc := &dockercontainer.HostConfig{
|
||||||
Binds: generateMountBindings(config.GetMounts()),
|
Binds: generateMountBindings(config.GetMounts()),
|
||||||
ReadonlyRootfs: config.GetReadonlyRootfs(),
|
|
||||||
Privileged: config.GetPrivileged(),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply options derived from the sandbox config.
|
// Apply cgroupsParent derived from the sandbox config.
|
||||||
if lc := sandboxConfig.GetLinux(); lc != nil {
|
if lc := sandboxConfig.GetLinux(); lc != nil {
|
||||||
// Apply Cgroup options.
|
// Apply Cgroup options.
|
||||||
// TODO: Check if this works with per-pod cgroups.
|
// 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...
|
// 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()
|
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.
|
// Apply Linux-specific options if applicable.
|
||||||
@ -167,6 +148,9 @@ func (ds *dockerService) CreateContainer(podSandboxID string, config *runtimeApi
|
|||||||
hc.OomScoreAdj = int(rOpts.GetOomScoreAdj())
|
hc.OomScoreAdj = int(rOpts.GetOomScoreAdj())
|
||||||
}
|
}
|
||||||
// Note: ShmSize is handled in kube_docker_client.go
|
// Note: ShmSize is handled in kube_docker_client.go
|
||||||
|
|
||||||
|
// Apply security context.
|
||||||
|
applyContainerSecurityContext(lc, podSandboxID, createConfig.Config, hc)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set devices for container.
|
// Set devices for container.
|
||||||
@ -180,12 +164,12 @@ func (ds *dockerService) CreateContainer(podSandboxID string, config *runtimeApi
|
|||||||
}
|
}
|
||||||
hc.Resources.Devices = devices
|
hc.Resources.Devices = devices
|
||||||
|
|
||||||
var err error
|
// Apply appArmor and seccomp options.
|
||||||
hc.SecurityOpt, err = getContainerSecurityOpts(config.Metadata.GetName(), sandboxConfig, ds.seccompProfileRoot)
|
securityOpts, err := getContainerSecurityOpts(config.Metadata.GetName(), sandboxConfig, ds.seccompProfileRoot)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("failed to generate container security options for container %q: %v", config.Metadata.GetName(), err)
|
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
|
createConfig.HostConfig = hc
|
||||||
createResp, err := ds.client.CreateContainer(createConfig)
|
createResp, err := ds.client.CreateContainer(createConfig)
|
||||||
|
@ -79,7 +79,7 @@ func (ds *dockerService) RunPodSandbox(config *runtimeApi.PodSandboxConfig) (str
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return createResp.ID, fmt.Errorf("failed to start sandbox container for pod %q: %v", config.Metadata.GetName(), err)
|
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
|
return createResp.ID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -286,6 +286,18 @@ func (ds *dockerService) ListPodSandbox(filter *runtimeApi.PodSandboxFilter) ([]
|
|||||||
return result, nil
|
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) {
|
func (ds *dockerService) makeSandboxDockerConfig(c *runtimeApi.PodSandboxConfig, image string) (*dockertypes.ContainerCreateConfig, error) {
|
||||||
// Merge annotations and labels because docker supports only labels.
|
// Merge annotations and labels because docker supports only labels.
|
||||||
labels := makeLabels(c.GetLabels(), c.GetAnnotations())
|
labels := makeLabels(c.GetLabels(), c.GetAnnotations())
|
||||||
@ -316,29 +328,11 @@ func (ds *dockerService) makeSandboxDockerConfig(c *runtimeApi.PodSandboxConfig,
|
|||||||
|
|
||||||
// Apply linux-specific options.
|
// Apply linux-specific options.
|
||||||
if lc := c.GetLinux(); lc != nil {
|
if lc := c.GetLinux(); lc != nil {
|
||||||
// Apply Cgroup options.
|
if err := ds.applySandboxLinuxOptions(hc, lc, createConfig, image); err != nil {
|
||||||
// TODO: Check if this works with per-pod cgroups.
|
return nil, err
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Set port mappings.
|
// Set port mappings.
|
||||||
exposedPorts, portBindings := makePortsAndBindings(c.GetPortMappings())
|
exposedPorts, portBindings := makePortsAndBindings(c.GetPortMappings())
|
||||||
createConfig.Config.ExposedPorts = exposedPorts
|
createConfig.Config.ExposedPorts = exposedPorts
|
||||||
@ -355,10 +349,11 @@ func (ds *dockerService) makeSandboxDockerConfig(c *runtimeApi.PodSandboxConfig,
|
|||||||
setSandboxResources(hc)
|
setSandboxResources(hc)
|
||||||
|
|
||||||
// Set security options.
|
// Set security options.
|
||||||
hc.SecurityOpt, err = getSandboxSecurityOpts(c, ds.seccompProfileRoot)
|
securityOpts, err := getSandboxSecurityOpts(c, ds.seccompProfileRoot)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to generate sandbox security options for sandbox %q: %v", c.Metadata.GetName(), err)
|
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
|
return createConfig, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,7 +186,13 @@ func TestHostNetworkPluginInvocation(t *testing.T) {
|
|||||||
map[string]string{"annotation": ns},
|
map[string]string{"annotation": ns},
|
||||||
)
|
)
|
||||||
hostNetwork := true
|
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))}
|
cID := kubecontainer.ContainerID{Type: runtimeName, ID: fmt.Sprintf("/%v", makeSandboxName(c))}
|
||||||
|
|
||||||
// No calls to network plugin are expected
|
// 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
|
// '<HostPath>:<ContainerPath>:Z', if the volume requires SELinux
|
||||||
// relabeling and the pod provides an SELinux label
|
// relabeling and the pod provides an SELinux label
|
||||||
func generateMountBindings(mounts []*runtimeApi.Mount) (result []string) {
|
func generateMountBindings(mounts []*runtimeApi.Mount) (result []string) {
|
||||||
// TODO: resolve podHasSELinuxLabel
|
|
||||||
for _, m := range mounts {
|
for _, m := range mounts {
|
||||||
bind := fmt.Sprintf("%s:%s", m.GetHostPath(), m.GetContainerPath())
|
bind := fmt.Sprintf("%s:%s", m.GetHostPath(), m.GetContainerPath())
|
||||||
readOnly := m.GetReadonly()
|
readOnly := m.GetReadonly()
|
||||||
if readOnly {
|
if readOnly {
|
||||||
bind += ":ro"
|
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 m.GetSelinuxRelabel() {
|
||||||
if readOnly {
|
if readOnly {
|
||||||
bind += ",Z"
|
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)
|
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 no user is defined container will run as root
|
||||||
if user == "" {
|
if user == "" {
|
||||||
return true, nil
|
return true, nil
|
||||||
@ -2488,8 +2488,8 @@ func (dm *DockerManager) isImageRoot(image string) (bool, error) {
|
|||||||
return uid == 0, nil
|
return uid == 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getUidFromUser splits the uid out of an uid:gid string.
|
// GetUidFromUser splits the uid out of an uid:gid string.
|
||||||
func getUidFromUser(id string) string {
|
func GetUidFromUser(id string) string {
|
||||||
if id == "" {
|
if id == "" {
|
||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
|
@ -1454,7 +1454,7 @@ func TestGetUidFromUser(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
for k, v := range tests {
|
for k, v := range tests {
|
||||||
actual := getUidFromUser(v.input)
|
actual := GetUidFromUser(v.input)
|
||||||
if actual != v.expect {
|
if actual != v.expect {
|
||||||
t.Errorf("%s failed. Expected %s but got %s", k, v.expect, actual)
|
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 {
|
if effectiveSC.SELinuxOptions != nil {
|
||||||
hostConfig.SecurityOpt = modifySecurityOption(hostConfig.SecurityOpt, dockerLabelUser, effectiveSC.SELinuxOptions.User)
|
hostConfig.SecurityOpt = ModifySecurityOptions(hostConfig.SecurityOpt, effectiveSC.SELinuxOptions)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
// modifySecurityOption adds the security option of name to the config array with value in the form
|
||||||
// of name:value
|
// of name:value
|
||||||
func modifySecurityOption(config []string, name, value string) []string {
|
func modifySecurityOption(config []string, name, value string) []string {
|
||||||
|
@ -104,10 +104,10 @@ func TestModifyHostConfig(t *testing.T) {
|
|||||||
|
|
||||||
setSELinuxHC := &dockercontainer.HostConfig{}
|
setSELinuxHC := &dockercontainer.HostConfig{}
|
||||||
setSELinuxHC.SecurityOpt = []string{
|
setSELinuxHC.SecurityOpt = []string{
|
||||||
fmt.Sprintf("%s:%s", dockerLabelUser, "user"),
|
fmt.Sprintf("%s:%s", DockerLabelUser, "user"),
|
||||||
fmt.Sprintf("%s:%s", dockerLabelRole, "role"),
|
fmt.Sprintf("%s:%s", DockerLabelRole, "role"),
|
||||||
fmt.Sprintf("%s:%s", dockerLabelType, "type"),
|
fmt.Sprintf("%s:%s", DockerLabelType, "type"),
|
||||||
fmt.Sprintf("%s:%s", dockerLabelLevel, "level"),
|
fmt.Sprintf("%s:%s", DockerLabelLevel, "level"),
|
||||||
}
|
}
|
||||||
|
|
||||||
// seLinuxLabelsSC := fullValidSecurityContext()
|
// seLinuxLabelsSC := fullValidSecurityContext()
|
||||||
@ -325,10 +325,10 @@ func fullValidHostConfig() *dockercontainer.HostConfig {
|
|||||||
CapAdd: []string{"addCapA", "addCapB"},
|
CapAdd: []string{"addCapA", "addCapB"},
|
||||||
CapDrop: []string{"dropCapA", "dropCapB"},
|
CapDrop: []string{"dropCapA", "dropCapB"},
|
||||||
SecurityOpt: []string{
|
SecurityOpt: []string{
|
||||||
fmt.Sprintf("%s:%s", dockerLabelUser, "user"),
|
fmt.Sprintf("%s:%s", DockerLabelUser, "user"),
|
||||||
fmt.Sprintf("%s:%s", dockerLabelRole, "role"),
|
fmt.Sprintf("%s:%s", DockerLabelRole, "role"),
|
||||||
fmt.Sprintf("%s:%s", dockerLabelType, "type"),
|
fmt.Sprintf("%s:%s", DockerLabelType, "type"),
|
||||||
fmt.Sprintf("%s:%s", dockerLabelLevel, "level"),
|
fmt.Sprintf("%s:%s", DockerLabelLevel, "level"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,9 +41,9 @@ type SecurityContextProvider interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
dockerLabelUser string = "label:user"
|
DockerLabelUser string = "label:user"
|
||||||
dockerLabelRole string = "label:role"
|
DockerLabelRole string = "label:role"
|
||||||
dockerLabelType string = "label:type"
|
DockerLabelType string = "label:type"
|
||||||
dockerLabelLevel string = "label:level"
|
DockerLabelLevel string = "label:level"
|
||||||
dockerLabelDisable string = "label:disable"
|
DockerLabelDisable string = "label:disable"
|
||||||
)
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user