add non-root directive to SC and kubelet checking

This commit is contained in:
Paul Weil
2015-08-10 13:30:34 -04:00
parent 72db123025
commit e490c20c22
11 changed files with 356 additions and 0 deletions

View File

@@ -1619,6 +1619,15 @@ func (dm *DockerManager) SyncPod(pod *api.Pod, runningPod kubecontainer.Pod, pod
continue
}
if container.SecurityContext != nil && container.SecurityContext.RunAsNonRoot {
err := dm.verifyNonRoot(container)
dm.updateReasonCache(pod, container, err)
if err != nil {
glog.Errorf("Error running pod %q container %q: %v", kubecontainer.GetPodFullName(pod), container.Name, err)
continue
}
}
// TODO(dawnchen): Check RestartPolicy.DelaySeconds before restart a container
namespaceMode := fmt.Sprintf("container:%v", podInfraContainerID)
_, err = dm.runContainerInPod(pod, container, namespaceMode, namespaceMode)
@@ -1635,3 +1644,62 @@ func (dm *DockerManager) SyncPod(pod *api.Pod, runningPod kubecontainer.Pod, pod
return nil
}
// verifyNonRoot returns an error if the container or image will run as the root user.
func (dm *DockerManager) verifyNonRoot(container *api.Container) error {
if securitycontext.HasRunAsUser(container) {
if securitycontext.HasRootRunAsUser(container) {
return fmt.Errorf("container's runAsUser breaks non-root policy")
}
return nil
}
imgRoot, err := dm.isImageRoot(container.Image)
if err != nil {
return err
}
if imgRoot {
return fmt.Errorf("container has no runAsUser and image will run as root")
}
return nil
}
// isImageRoot returns true if the user directive is not set on the image, the user is set to 0
// or the user is set to root. If there is an error inspecting the image this method will return
// false and return the error.
func (dm *DockerManager) isImageRoot(image string) (bool, error) {
img, err := dm.client.InspectImage(image)
if err != nil {
return false, err
}
if img == nil || img.Config == nil {
return false, fmt.Errorf("unable to inspect image %s, nil Config", image)
}
user := getUidFromUser(img.Config.User)
// if no user is defined container will run as root
if user == "" {
return true, nil
}
// do not allow non-numeric user directives
uid, err := strconv.Atoi(user)
if err != nil {
return false, fmt.Errorf("unable to validate image is non-root, non-numeric user (%s) is not allowed", user)
}
// user is numeric, check for 0
return uid == 0, nil
}
// getUidFromUser splits the uid out of a uid:gid string.
func getUidFromUser(id string) string {
if id == "" {
return id
}
// split instances where the id may contain uid:gid
if strings.Contains(id, ":") {
return strings.Split(id, ":")[0]
}
// no gid, just return the id
return id
}

View File

@@ -2085,3 +2085,129 @@ func TestGetPodStatusSortedContainers(t *testing.T) {
}
}
}
func TestVerifyNonRoot(t *testing.T) {
dm, fakeDocker := newTestDockerManager()
// setup test cases.
var rootUid int64 = 0
var nonRootUid int64 = 1
tests := map[string]struct {
container *api.Container
inspectImage *docker.Image
expectedError string
}{
// success cases
"non-root runAsUser": {
container: &api.Container{
SecurityContext: &api.SecurityContext{
RunAsUser: &nonRootUid,
},
},
},
"numeric non-root image user": {
container: &api.Container{},
inspectImage: &docker.Image{
Config: &docker.Config{
User: "1",
},
},
},
"numeric non-root image user with gid": {
container: &api.Container{},
inspectImage: &docker.Image{
Config: &docker.Config{
User: "1:2",
},
},
},
// failure cases
"root runAsUser": {
container: &api.Container{
SecurityContext: &api.SecurityContext{
RunAsUser: &rootUid,
},
},
expectedError: "container's runAsUser breaks non-root policy",
},
"non-numeric image user": {
container: &api.Container{},
inspectImage: &docker.Image{
Config: &docker.Config{
User: "foo",
},
},
expectedError: "unable to validate image is non-root, non-numeric user",
},
"numeric root image user": {
container: &api.Container{},
inspectImage: &docker.Image{
Config: &docker.Config{
User: "0",
},
},
expectedError: "container has no runAsUser and image will run as root",
},
"numeric root image user with gid": {
container: &api.Container{},
inspectImage: &docker.Image{
Config: &docker.Config{
User: "0:1",
},
},
expectedError: "container has no runAsUser and image will run as root",
},
"nil image in inspect": {
container: &api.Container{},
expectedError: "unable to inspect image",
},
"nil config in image inspect": {
container: &api.Container{},
inspectImage: &docker.Image{},
expectedError: "unable to inspect image",
},
}
for k, v := range tests {
fakeDocker.Image = v.inspectImage
err := dm.verifyNonRoot(v.container)
if v.expectedError == "" && err != nil {
t.Errorf("%s had unexpected error %v", k, err)
}
if v.expectedError != "" && !strings.Contains(err.Error(), v.expectedError) {
t.Errorf("%s expected error %s but received %s", k, v.expectedError, err.Error())
}
}
}
func TestGetUidFromUser(t *testing.T) {
tests := map[string]struct {
input string
expect string
}{
"no gid": {
input: "0",
expect: "0",
},
"uid/gid": {
input: "0:1",
expect: "0",
},
"empty input": {
input: "",
expect: "",
},
"multiple spearators": {
input: "1:2:3",
expect: "1",
},
}
for k, v := range tests {
actual := getUidFromUser(v.input)
if actual != v.expect {
t.Errorf("%s failed. Expected %s but got %s", k, v.expect, actual)
}
}
}