mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-25 04:33:26 +00:00
Add separate username field in CRI and use it.
This commit is contained in:
parent
c79b8afe5b
commit
2ce5deb6fd
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -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",
|
||||
],
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user