mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-26 05:03:09 +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"
|
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
|
||||||
@ -57,14 +56,15 @@ func imageInspectToRuntimeAPIImage(image *dockertypes.ImageInspect) (*runtimeApi
|
|||||||
}
|
}
|
||||||
|
|
||||||
size := uint64(image.VirtualSize)
|
size := uint64(image.VirtualSize)
|
||||||
user := dockertools.GetUserFromImageUser(image.Config.User)
|
runtimeImage := &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,
|
||||||
User: &user,
|
}
|
||||||
}, nil
|
|
||||||
|
runtimeImage.Uid, runtimeImage.Username = getUserFromImageUser(image.Config.User)
|
||||||
|
return runtimeImage, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func toPullableImageID(id string, image *dockertypes.ImageInspect) string {
|
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) {
|
func (f *dockerFilter) AddLabel(key, value string) {
|
||||||
f.Add("label", fmt.Sprintf("%s=%s", key, value))
|
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)
|
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.
|
// modifyContainerConfig applies container security context config to dockercontainer.Config.
|
||||||
func modifyContainerConfig(sc *runtimeapi.LinuxContainerSecurityContext, config *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.
|
// modifyHostConfig applies security context config to dockercontainer.HostConfig.
|
||||||
|
@ -18,6 +18,7 @@ package dockershim
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
dockercontainer "github.com/docker/engine-api/types/container"
|
dockercontainer "github.com/docker/engine-api/types/container"
|
||||||
@ -28,7 +29,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestModifyContainerConfig(t *testing.T) {
|
func TestModifyContainerConfig(t *testing.T) {
|
||||||
var uid string = "123"
|
var uid int64 = 123
|
||||||
|
var username string = "testuser"
|
||||||
|
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
name string
|
name string
|
||||||
@ -41,7 +43,16 @@ func TestModifyContainerConfig(t *testing.T) {
|
|||||||
RunAsUser: &uid,
|
RunAsUser: &uid,
|
||||||
},
|
},
|
||||||
expected: &dockercontainer.Config{
|
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/coreos/go-semver/semver",
|
||||||
"//vendor:github.com/docker/docker/pkg/jsonlog",
|
"//vendor:github.com/docker/docker/pkg/jsonlog",
|
||||||
"//vendor:github.com/fsnotify/fsnotify",
|
"//vendor:github.com/fsnotify/fsnotify",
|
||||||
"//vendor:github.com/gogo/protobuf/proto",
|
|
||||||
"//vendor:github.com/golang/glog",
|
"//vendor:github.com/golang/glog",
|
||||||
"//vendor:github.com/google/cadvisor/info/v1",
|
"//vendor:github.com/google/cadvisor/info/v1",
|
||||||
],
|
],
|
||||||
|
@ -146,19 +146,24 @@ func getContainerSpec(pod *api.Pod, containerName string) *api.Container {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getImageUID gets uid that will run the command(s) from image.
|
// getImageUser gets uid or user name that will run the command(s) from image. The function
|
||||||
func (m *kubeGenericRuntimeManager) getImageUser(image string) (string, error) {
|
// 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})
|
imageStatus, err := m.imageService.ImageStatus(&runtimeApi.ImageSpec{Image: &image})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
user := imageStatus.GetUser()
|
if imageStatus != nil && imageStatus.Uid != nil {
|
||||||
// kuberuntime treats empty user as root.
|
// If uid is set, return uid.
|
||||||
if user == "" {
|
return imageStatus.Uid, nil, nil
|
||||||
return "0", 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.
|
// 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
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify RunAsNonRoot.
|
uid, username, err := m.getImageUser(container.Image)
|
||||||
imageUser, err := m.getImageUser(container.Image)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := verifyRunAsNonRoot(pod, container, imageUser); err != nil {
|
if uid != nil {
|
||||||
return nil, err
|
// 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)
|
command, args := kubecontainer.ExpandContainerCommandAndArgs(container, opts.Envs)
|
||||||
@ -164,7 +168,7 @@ func (m *kubeGenericRuntimeManager) generateContainerConfig(container *api.Conta
|
|||||||
Stdin: &container.Stdin,
|
Stdin: &container.Stdin,
|
||||||
StdinOnce: &container.StdinOnce,
|
StdinOnce: &container.StdinOnce,
|
||||||
Tty: &container.TTY,
|
Tty: &container.TTY,
|
||||||
Linux: m.generateLinuxContainerConfig(container, pod, imageUser),
|
Linux: m.generateLinuxContainerConfig(container, pod, uid, username),
|
||||||
}
|
}
|
||||||
|
|
||||||
// set environment variables
|
// set environment variables
|
||||||
@ -182,10 +186,10 @@ func (m *kubeGenericRuntimeManager) generateContainerConfig(container *api.Conta
|
|||||||
}
|
}
|
||||||
|
|
||||||
// generateLinuxContainerConfig generates linux container config for kubelet runtime api.
|
// 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{
|
lc := &runtimeApi.LinuxContainerConfig{
|
||||||
Resources: &runtimeApi.LinuxContainerResources{},
|
Resources: &runtimeApi.LinuxContainerResources{},
|
||||||
SecurityContext: m.determineEffectiveSecurityContext(pod, container, imageUser),
|
SecurityContext: m.determineEffectiveSecurityContext(pod, container, uid, username),
|
||||||
}
|
}
|
||||||
|
|
||||||
// set linux container resources
|
// set linux container resources
|
||||||
|
@ -146,7 +146,7 @@ func (m *kubeGenericRuntimeManager) generatePodSandboxLinuxConfig(pod *api.Pod,
|
|||||||
HostIpc: &sc.HostIPC,
|
HostIpc: &sc.HostIPC,
|
||||||
HostPid: &sc.HostPID,
|
HostPid: &sc.HostPID,
|
||||||
},
|
},
|
||||||
RunAsUser: convertToRuntimeRunAsUser(sc.RunAsUser),
|
RunAsUser: sc.RunAsUser,
|
||||||
}
|
}
|
||||||
|
|
||||||
if sc.FSGroup != nil {
|
if sc.FSGroup != nil {
|
||||||
|
@ -18,10 +18,6 @@ package kuberuntime
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/gogo/protobuf/proto"
|
|
||||||
"github.com/golang/glog"
|
|
||||||
|
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
runtimeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
|
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.
|
// 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)
|
effectiveSc := securitycontext.DetermineEffectiveSecurityContext(pod, container)
|
||||||
synthesized := convertToRuntimeSecurityContext(effectiveSc)
|
synthesized := convertToRuntimeSecurityContext(effectiveSc)
|
||||||
if synthesized == nil {
|
if synthesized == nil {
|
||||||
@ -38,7 +34,8 @@ func (m *kubeGenericRuntimeManager) determineEffectiveSecurityContext(pod *api.P
|
|||||||
|
|
||||||
// set RunAsUser.
|
// set RunAsUser.
|
||||||
if synthesized.RunAsUser == nil {
|
if synthesized.RunAsUser == nil {
|
||||||
synthesized.RunAsUser = &imageUser
|
synthesized.RunAsUser = uid
|
||||||
|
synthesized.RunAsUsername = username
|
||||||
}
|
}
|
||||||
|
|
||||||
// set namespace options and supplemental groups.
|
// set namespace options and supplemental groups.
|
||||||
@ -65,7 +62,7 @@ func (m *kubeGenericRuntimeManager) determineEffectiveSecurityContext(pod *api.P
|
|||||||
}
|
}
|
||||||
|
|
||||||
// verifyRunAsNonRoot verifies RunAsNonRoot.
|
// 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)
|
effectiveSc := securitycontext.DetermineEffectiveSecurityContext(pod, container)
|
||||||
if effectiveSc == nil || effectiveSc.RunAsNonRoot == nil {
|
if effectiveSc == nil || effectiveSc.RunAsNonRoot == nil {
|
||||||
return nil
|
return nil
|
||||||
@ -78,15 +75,6 @@ func verifyRunAsNonRoot(pod *api.Pod, container *api.Container, imageUser string
|
|||||||
return nil
|
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 {
|
if uid == 0 {
|
||||||
return fmt.Errorf("container has runAsNonRoot and image will run as root")
|
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{
|
return &runtimeapi.LinuxContainerSecurityContext{
|
||||||
RunAsUser: convertToRuntimeRunAsUser(securityContext.RunAsUser),
|
RunAsUser: securityContext.RunAsUser,
|
||||||
Privileged: securityContext.Privileged,
|
Privileged: securityContext.Privileged,
|
||||||
ReadonlyRootfs: securityContext.ReadOnlyRootFilesystem,
|
ReadonlyRootfs: securityContext.ReadOnlyRootFilesystem,
|
||||||
Capabilities: convertToRuntimeCapabilities(securityContext.Capabilities),
|
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.
|
// convertToRuntimeSELinuxOption converts api.SELinuxOptions to runtimeapi.SELinuxOption.
|
||||||
func convertToRuntimeSELinuxOption(opts *api.SELinuxOptions) *runtimeapi.SELinuxOption {
|
func convertToRuntimeSELinuxOption(opts *api.SELinuxOptions) *runtimeapi.SELinuxOption {
|
||||||
if opts == nil {
|
if opts == nil {
|
||||||
|
Loading…
Reference in New Issue
Block a user