Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

CRI: Support string user name. #36423

Merged
merged 1 commit into from
Nov 9, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
448 changes: 225 additions & 223 deletions pkg/kubelet/api/v1alpha1/runtime/api.pb.go

Large diffs are not rendered by default.

14 changes: 8 additions & 6 deletions pkg/kubelet/api/v1alpha1/runtime/api.proto
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,9 @@ message LinuxSandboxSecurityContext {
optional NamespaceOption namespace_options = 1;
// Optional SELinux context to be applied.
optional SELinuxOption selinux_options = 2;
// The UID to run the entrypoint of the sandbox process.
optional int64 run_as_user = 3;
// The user to run the entrypoint of the sandbox process, it could be uid or
// user name.
optional string run_as_user = 3;
// If set, the root filesystem of the sandbox is read-only.
optional bool readonly_rootfs = 4;
// A list of groups applied to the first process run in the sandbox, in addition
Expand Down Expand Up @@ -439,9 +440,10 @@ message LinuxContainerSecurityContext {
optional NamespaceOption namespace_options = 3;
// Optional SELinux context to be applied.
optional SELinuxOption selinux_options = 4;
// The UID to run the the container process as.
// The user to run the the container process as, it could be uid or user
// name.
// Defaults to user specified in image metadata if unspecified.
optional int64 run_as_user = 5;
optional string run_as_user = 5;
// If set, the root filesystem of the container is read-only.
optional bool readonly_rootfs = 6;
// A list of groups applied to the first process run in the container, in addition
Expand Down Expand Up @@ -758,8 +760,8 @@ message Image {
repeated string repo_digests = 3;
// The size of the image in bytes.
optional uint64 size = 4;
// The uid that will run the command(s).
optional int64 uid = 5;
// The user that will run the command(s).
optional string user = 5;
}

message ListImagesResponse {
Expand Down
15 changes: 2 additions & 13 deletions pkg/kubelet/dockershim/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ package dockershim

import (
"fmt"
"strconv"
"strings"
"time"

Expand Down Expand Up @@ -57,24 +56,14 @@ 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)
}
}
user := dockertools.GetUserFromImageUser(image.Config.User)
return &runtimeApi.Image{
Id: &image.ID,
RepoTags: image.RepoTags,
RepoDigests: image.RepoDigests,
Size_: &size,
Uid: &uid,
User: &user,
}, nil
}

Expand Down
4 changes: 1 addition & 3 deletions pkg/kubelet/dockershim/security_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,7 @@ func applyContainerSecurityContext(lc *runtimeapi.LinuxContainerConfig, sandboxI

// 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)
}
config.User = sc.GetRunAsUser()
}

// modifyHostConfig applies security context config to dockercontainer.HostConfig.
Expand Down
5 changes: 2 additions & 3 deletions pkg/kubelet/dockershim/security_context_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ package dockershim

import (
"fmt"
"strconv"
"testing"

dockercontainer "github.com/docker/engine-api/types/container"
Expand All @@ -29,7 +28,7 @@ import (
)

func TestModifyContainerConfig(t *testing.T) {
var uid int64 = 123
var uid string = "123"

cases := []struct {
name string
Expand All @@ -42,7 +41,7 @@ func TestModifyContainerConfig(t *testing.T) {
RunAsUser: &uid,
},
expected: &dockercontainer.Config{
User: strconv.FormatInt(uid, 10),
User: uid,
},
},
{
Expand Down
10 changes: 5 additions & 5 deletions pkg/kubelet/dockertools/docker_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 := GetUserFromImageUser(img.Config.User)
// if no user is defined container will run as root
if user == "" {
return true, nil
Expand All @@ -2488,16 +2488,16 @@ 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 {
// GetUserFromImageUser splits the user out of an user:group string.
func GetUserFromImageUser(id string) string {
if id == "" {
return id
}
// split instances where the id may contain uid:gid
// split instances where the id may contain user:group
if strings.Contains(id, ":") {
return strings.Split(id, ":")[0]
}
// no gid, just return the id
// no group, just return the id
return id
}

Expand Down
12 changes: 10 additions & 2 deletions pkg/kubelet/dockertools/docker_manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1431,7 +1431,7 @@ func TestVerifyNonRoot(t *testing.T) {
}
}

func TestGetUidFromUser(t *testing.T) {
func TestGetUserFromImageUser(t *testing.T) {
tests := map[string]struct {
input string
expect string
Expand All @@ -1452,9 +1452,17 @@ func TestGetUidFromUser(t *testing.T) {
input: "1:2:3",
expect: "1",
},
"root username": {
input: "root:root",
expect: "root",
},
"username": {
input: "test:test",
expect: "test",
},
}
for k, v := range tests {
actual := GetUidFromUser(v.input)
actual := GetUserFromImageUser(v.input)
if actual != v.expect {
t.Errorf("%s failed. Expected %s but got %s", k, v.expect, actual)
}
Expand Down
1 change: 1 addition & 0 deletions pkg/kubelet/kuberuntime/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ go_library(
"//vendor:github.com/coreos/go-semver/semver",
"//vendor:github.com/docker/docker/pkg/jsonlog",
"//vendor:github.com/fsnotify/fsnotify",
"//vendor:github.com/gogo/protobuf/proto",
"//vendor:github.com/golang/glog",
"//vendor:github.com/google/cadvisor/info/v1",
],
Expand Down
11 changes: 8 additions & 3 deletions pkg/kubelet/kuberuntime/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,13 +147,18 @@ func getContainerSpec(pod *api.Pod, containerName string) *api.Container {
}

// getImageUID gets uid that will run the command(s) from image.
func (m *kubeGenericRuntimeManager) getImageUser(image string) (int64, error) {
func (m *kubeGenericRuntimeManager) getImageUser(image string) (string, error) {
imageStatus, err := m.imageService.ImageStatus(&runtimeApi.ImageSpec{Image: &image})
if err != nil {
return 0, err
return "", err
}

return imageStatus.GetUid(), nil
user := imageStatus.GetUser()
// kuberuntime treats empty user as root.
if user == "" {
return "0", nil
}
return user, nil
}

// isContainerFailed returns true if container has exited and exitcode is not zero.
Expand Down
2 changes: 1 addition & 1 deletion pkg/kubelet/kuberuntime/kuberuntime_container.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ func (m *kubeGenericRuntimeManager) generateContainerConfig(container *api.Conta
}

// generateLinuxContainerConfig generates linux container config for kubelet runtime api.
func (m *kubeGenericRuntimeManager) generateLinuxContainerConfig(container *api.Container, pod *api.Pod, imageUser int64) *runtimeApi.LinuxContainerConfig {
func (m *kubeGenericRuntimeManager) generateLinuxContainerConfig(container *api.Container, pod *api.Pod, imageUser string) *runtimeApi.LinuxContainerConfig {
lc := &runtimeApi.LinuxContainerConfig{
Resources: &runtimeApi.LinuxContainerResources{},
SecurityContext: m.determineEffectiveSecurityContext(pod, container, imageUser),
Expand Down
2 changes: 1 addition & 1 deletion pkg/kubelet/kuberuntime/kuberuntime_sandbox.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ func (m *kubeGenericRuntimeManager) generatePodSandboxLinuxConfig(pod *api.Pod,
HostIpc: &sc.HostIPC,
HostPid: &sc.HostPID,
},
RunAsUser: sc.RunAsUser,
RunAsUser: convertToRuntimeRunAsUser(sc.RunAsUser),
}

if sc.FSGroup != nil {
Expand Down
36 changes: 30 additions & 6 deletions pkg/kubelet/kuberuntime/security_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,18 @@ package kuberuntime

import (
"fmt"
"strconv"

"github.com/gogo/protobuf/proto"
"github.com/golang/glog"

"k8s.io/kubernetes/pkg/api"
runtimeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
"k8s.io/kubernetes/pkg/securitycontext"
)

// determineEffectiveSecurityContext gets container's security context from api.Pod and api.Container.
func (m *kubeGenericRuntimeManager) determineEffectiveSecurityContext(pod *api.Pod, container *api.Container, imageUser int64) *runtimeapi.LinuxContainerSecurityContext {
func (m *kubeGenericRuntimeManager) determineEffectiveSecurityContext(pod *api.Pod, container *api.Container, imageUser string) *runtimeapi.LinuxContainerSecurityContext {
effectiveSc := securitycontext.DetermineEffectiveSecurityContext(pod, container)
synthesized := convertToRuntimeSecurityContext(effectiveSc)
if synthesized == nil {
Expand Down Expand Up @@ -61,17 +65,29 @@ func (m *kubeGenericRuntimeManager) determineEffectiveSecurityContext(pod *api.P
}

// verifyRunAsNonRoot verifies RunAsNonRoot.
func verifyRunAsNonRoot(pod *api.Pod, container *api.Container, imageUser int64) error {
func verifyRunAsNonRoot(pod *api.Pod, container *api.Container, imageUser string) error {
effectiveSc := securitycontext.DetermineEffectiveSecurityContext(pod, container)
if effectiveSc == nil || effectiveSc.RunAsNonRoot == nil {
return nil
}

if effectiveSc.RunAsUser != nil && *effectiveSc.RunAsUser == 0 {
return fmt.Errorf("container's runAsUser breaks non-root policy")
if effectiveSc.RunAsUser != nil {
if *effectiveSc.RunAsUser == 0 {
return fmt.Errorf("container's runAsUser breaks non-root policy")
}
return nil
}

// Non-root verification only supports numeric user now. For non-numeric user,
// just return nil to by-pass the verfication.
// TODO: Support non-numeric user.
uid, err := strconv.ParseInt(imageUser, 10, 64)
if err != nil {
glog.Warningf("Non-root verification doesn't support non-numeric user (%s)", imageUser)
return nil
}

if imageUser == 0 {
if uid == 0 {
return fmt.Errorf("container has runAsNonRoot and image will run as root")
}

Expand All @@ -85,14 +101,22 @@ func convertToRuntimeSecurityContext(securityContext *api.SecurityContext) *runt
}

return &runtimeapi.LinuxContainerSecurityContext{
RunAsUser: securityContext.RunAsUser,
RunAsUser: convertToRuntimeRunAsUser(securityContext.RunAsUser),
Privileged: securityContext.Privileged,
ReadonlyRootfs: securityContext.ReadOnlyRootFilesystem,
Capabilities: convertToRuntimeCapabilities(securityContext.Capabilities),
SelinuxOptions: convertToRuntimeSELinuxOption(securityContext.SELinuxOptions),
}
}

// convertToRuntimeRunAsUser converts RunAsUser from *int64 to *string.
func convertToRuntimeRunAsUser(runAsUser *int64) *string {
if runAsUser == nil {
return nil
}
return proto.String(strconv.FormatInt(*runAsUser, 10))
}

// convertToRuntimeSELinuxOption converts api.SELinuxOptions to runtimeapi.SELinuxOption.
func convertToRuntimeSELinuxOption(opts *api.SELinuxOptions) *runtimeapi.SELinuxOption {
if opts == nil {
Expand Down