Add separate username field in CRI and use it.

This commit is contained in:
Random-Liu 2016-11-14 15:33:22 -08:00
parent c79b8afe5b
commit 2ce5deb6fd
10 changed files with 115 additions and 50 deletions

View File

@ -24,7 +24,6 @@ import (
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
@ -57,14 +56,15 @@ func imageInspectToRuntimeAPIImage(image *dockertypes.ImageInspect) (*runtimeApi
}
size := uint64(image.VirtualSize)
user := dockertools.GetUserFromImageUser(image.Config.User)
return &runtimeApi.Image{
runtimeImage := &runtimeApi.Image{
Id: &image.ID,
RepoTags: image.RepoTags,
RepoDigests: image.RepoDigests,
Size_: &size,
User: &user,
}, nil
}
runtimeImage.Uid, runtimeImage.Username = getUserFromImageUser(image.Config.User)
return runtimeImage, nil
}
func toPullableImageID(id string, image *dockertypes.ImageInspect) string {

View File

@ -264,3 +264,21 @@ func (f *dockerFilter) Add(key, value string) {
func (f *dockerFilter) AddLabel(key, value string) {
f.Add("label", fmt.Sprintf("%s=%s", key, value))
}
// getUserFromImageUser gets uid or user name of the image user.
// If user is numeric, it will be treated as uid; or else, it is treated as user name.
func getUserFromImageUser(imageUser string) (*int64, *string) {
user := dockertools.GetUserFromImageUser(imageUser)
// return both nil if user is not specified in the image.
if user == "" {
return nil, nil
}
// user could be either uid or user name. Try to interpret as numeric uid.
uid, err := strconv.ParseInt(user, 10, 64)
if err != nil {
// If user is non numeric, assume it's user name.
return nil, &user
}
// If user is a numeric uid.
return &uid, nil
}

View File

@ -187,3 +187,43 @@ func TestGetSystclsFromAnnotations(t *testing.T) {
assert.Equal(t, test.expectedSysctls, actual, "TestCase[%d]", i)
}
}
// TestGetUserFromImageUser tests the logic of getting image uid or user name of image user.
func TestGetUserFromImageUser(t *testing.T) {
newI64 := func(i int64) *int64 { return &i }
newStr := func(s string) *string { return &s }
for c, test := range map[string]struct {
user string
uid *int64
name *string
}{
"no gid": {
user: "0",
uid: newI64(0),
},
"uid/gid": {
user: "0:1",
uid: newI64(0),
},
"empty user": {
user: "",
},
"multiple spearators": {
user: "1:2:3",
uid: newI64(1),
},
"root username": {
user: "root:root",
name: newStr("root"),
},
"username": {
user: "test:test",
name: newStr("test"),
},
} {
t.Logf("TestCase - %q", c)
actualUID, actualName := getUserFromImageUser(test.user)
assert.Equal(t, test.uid, actualUID)
assert.Equal(t, test.name, actualName)
}
}

View File

@ -61,7 +61,15 @@ func applyContainerSecurityContext(lc *runtimeapi.LinuxContainerConfig, sandboxI
// modifyContainerConfig applies container security context config to dockercontainer.Config.
func modifyContainerConfig(sc *runtimeapi.LinuxContainerSecurityContext, config *dockercontainer.Config) {
config.User = sc.GetRunAsUser()
if sc == nil {
return
}
if sc.RunAsUser != nil {
config.User = strconv.FormatInt(sc.GetRunAsUser(), 10)
}
if sc.RunAsUsername != nil {
config.User = sc.GetRunAsUsername()
}
}
// modifyHostConfig applies security context config to dockercontainer.HostConfig.

View File

@ -18,6 +18,7 @@ package dockershim
import (
"fmt"
"strconv"
"testing"
dockercontainer "github.com/docker/engine-api/types/container"
@ -28,7 +29,8 @@ import (
)
func TestModifyContainerConfig(t *testing.T) {
var uid string = "123"
var uid int64 = 123
var username string = "testuser"
cases := []struct {
name string
@ -41,7 +43,16 @@ func TestModifyContainerConfig(t *testing.T) {
RunAsUser: &uid,
},
expected: &dockercontainer.Config{
User: uid,
User: strconv.FormatInt(uid, 10),
},
},
{
name: "container.SecurityContext.RunAsUsername set",
sc: &runtimeapi.LinuxContainerSecurityContext{
RunAsUsername: &username,
},
expected: &dockercontainer.Config{
User: username,
},
},
{

View File

@ -58,7 +58,6 @@ 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",
],

View File

@ -146,19 +146,24 @@ func getContainerSpec(pod *api.Pod, containerName string) *api.Container {
return nil
}
// getImageUID gets uid that will run the command(s) from image.
func (m *kubeGenericRuntimeManager) getImageUser(image string) (string, error) {
// getImageUser gets uid or user name that will run the command(s) from image. The function
// guarantees that only one of them is set.
func (m *kubeGenericRuntimeManager) getImageUser(image string) (*int64, *string, error) {
imageStatus, err := m.imageService.ImageStatus(&runtimeApi.ImageSpec{Image: &image})
if err != nil {
return "", err
return nil, nil, err
}
user := imageStatus.GetUser()
// kuberuntime treats empty user as root.
if user == "" {
return "0", nil
if imageStatus != nil && imageStatus.Uid != nil {
// If uid is set, return uid.
return imageStatus.Uid, nil, nil
}
return user, nil
if imageStatus != nil && imageStatus.Username != nil {
// If uid is not set, but user name is set, return user name.
return nil, imageStatus.Username, nil
}
// If non of them is set, treat it as root.
return new(int64), nil, nil
}
// isContainerFailed returns true if container has exited and exitcode is not zero.

View File

@ -135,13 +135,17 @@ func (m *kubeGenericRuntimeManager) generateContainerConfig(container *api.Conta
return nil, err
}
// Verify RunAsNonRoot.
imageUser, err := m.getImageUser(container.Image)
uid, username, err := m.getImageUser(container.Image)
if err != nil {
return nil, err
}
if err := verifyRunAsNonRoot(pod, container, imageUser); err != nil {
return nil, err
if uid != nil {
// Verify RunAsNonRoot. Non-root verification only supports numeric user.
if err := verifyRunAsNonRoot(pod, container, *uid); err != nil {
return nil, err
}
} else {
glog.Warningf("Non-root verification doesn't support non-numeric user (%s)", *username)
}
command, args := kubecontainer.ExpandContainerCommandAndArgs(container, opts.Envs)
@ -164,7 +168,7 @@ func (m *kubeGenericRuntimeManager) generateContainerConfig(container *api.Conta
Stdin: &container.Stdin,
StdinOnce: &container.StdinOnce,
Tty: &container.TTY,
Linux: m.generateLinuxContainerConfig(container, pod, imageUser),
Linux: m.generateLinuxContainerConfig(container, pod, uid, username),
}
// set environment variables
@ -182,10 +186,10 @@ 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 string) *runtimeApi.LinuxContainerConfig {
func (m *kubeGenericRuntimeManager) generateLinuxContainerConfig(container *api.Container, pod *api.Pod, uid *int64, username *string) *runtimeApi.LinuxContainerConfig {
lc := &runtimeApi.LinuxContainerConfig{
Resources: &runtimeApi.LinuxContainerResources{},
SecurityContext: m.determineEffectiveSecurityContext(pod, container, imageUser),
SecurityContext: m.determineEffectiveSecurityContext(pod, container, uid, username),
}
// set linux container resources

View File

@ -146,7 +146,7 @@ func (m *kubeGenericRuntimeManager) generatePodSandboxLinuxConfig(pod *api.Pod,
HostIpc: &sc.HostIPC,
HostPid: &sc.HostPID,
},
RunAsUser: convertToRuntimeRunAsUser(sc.RunAsUser),
RunAsUser: sc.RunAsUser,
}
if sc.FSGroup != nil {

View File

@ -18,10 +18,6 @@ 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"
@ -29,7 +25,7 @@ import (
)
// determineEffectiveSecurityContext gets container's security context from api.Pod and api.Container.
func (m *kubeGenericRuntimeManager) determineEffectiveSecurityContext(pod *api.Pod, container *api.Container, imageUser string) *runtimeapi.LinuxContainerSecurityContext {
func (m *kubeGenericRuntimeManager) determineEffectiveSecurityContext(pod *api.Pod, container *api.Container, uid *int64, username *string) *runtimeapi.LinuxContainerSecurityContext {
effectiveSc := securitycontext.DetermineEffectiveSecurityContext(pod, container)
synthesized := convertToRuntimeSecurityContext(effectiveSc)
if synthesized == nil {
@ -38,7 +34,8 @@ func (m *kubeGenericRuntimeManager) determineEffectiveSecurityContext(pod *api.P
// set RunAsUser.
if synthesized.RunAsUser == nil {
synthesized.RunAsUser = &imageUser
synthesized.RunAsUser = uid
synthesized.RunAsUsername = username
}
// set namespace options and supplemental groups.
@ -65,7 +62,7 @@ func (m *kubeGenericRuntimeManager) determineEffectiveSecurityContext(pod *api.P
}
// verifyRunAsNonRoot verifies RunAsNonRoot.
func verifyRunAsNonRoot(pod *api.Pod, container *api.Container, imageUser string) error {
func verifyRunAsNonRoot(pod *api.Pod, container *api.Container, uid int64) error {
effectiveSc := securitycontext.DetermineEffectiveSecurityContext(pod, container)
if effectiveSc == nil || effectiveSc.RunAsNonRoot == nil {
return nil
@ -78,15 +75,6 @@ func verifyRunAsNonRoot(pod *api.Pod, container *api.Container, imageUser string
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 uid == 0 {
return fmt.Errorf("container has runAsNonRoot and image will run as root")
}
@ -101,7 +89,7 @@ func convertToRuntimeSecurityContext(securityContext *api.SecurityContext) *runt
}
return &runtimeapi.LinuxContainerSecurityContext{
RunAsUser: convertToRuntimeRunAsUser(securityContext.RunAsUser),
RunAsUser: securityContext.RunAsUser,
Privileged: securityContext.Privileged,
ReadonlyRootfs: securityContext.ReadOnlyRootFilesystem,
Capabilities: convertToRuntimeCapabilities(securityContext.Capabilities),
@ -109,14 +97,6 @@ func convertToRuntimeSecurityContext(securityContext *api.SecurityContext) *runt
}
}
// 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 {