diff --git a/pkg/podimpersonation/podimpersonation.go b/pkg/podimpersonation/podimpersonation.go index e3e29c0c1..c01da0f40 100644 --- a/pkg/podimpersonation/podimpersonation.go +++ b/pkg/podimpersonation/podimpersonation.go @@ -573,6 +573,9 @@ func (s *PodImpersonation) augmentPod(pod *v1.Pod, sa *v1.ServiceAccount, secret continue } + //This mounts two volumes, one configMap and one emptyDir. + //The reason for this is that we need to change the permissions on the kubeconfig file + //and, since a configMap volume is always read-only, we need an emptyDir volume as well. vmount := v1.VolumeMount{ Name: "user-kubeconfig", MountPath: "/tmp/.kube", @@ -583,21 +586,18 @@ func (s *PodImpersonation) augmentPod(pod *v1.Pod, sa *v1.ServiceAccount, secret SubPath: "config", } - pod.Spec.InitContainers = []v1.Container{ - { - Name: "init-kubeconfig-volume", - Image: image, - Command: []string{"sh", "-c", fmt.Sprintf("cp %s %s && chown %d %s/config", cfgVMount.MountPath, vmount.MountPath, shellUser, vmount.MountPath)}, - Resources: v1.ResourceRequirements{}, - ResizePolicy: nil, - ImagePullPolicy: v1.PullIfNotPresent, - SecurityContext: &v1.SecurityContext{ - RunAsUser: &zero, - RunAsGroup: &zero, - }, - VolumeMounts: []v1.VolumeMount{cfgVMount, vmount}, + pod.Spec.InitContainers = append(pod.Spec.InitContainers, v1.Container{ + Name: "init-kubeconfig-volume", + Image: image, + Command: []string{"sh", "-c", fmt.Sprintf("cp %s %s && chown %d %s/config", cfgVMount.MountPath, vmount.MountPath, shellUser, vmount.MountPath)}, + ImagePullPolicy: v1.PullIfNotPresent, + SecurityContext: &v1.SecurityContext{ + RunAsUser: &zero, + RunAsGroup: &zero, }, - } + VolumeMounts: []v1.VolumeMount{cfgVMount, vmount}, + }, + ) pod.Spec.Containers[i].VolumeMounts = append(container.VolumeMounts, v1.VolumeMount{ Name: "user-kubeconfig", diff --git a/pkg/podimpersonation/podimpersonation_test.go b/pkg/podimpersonation/podimpersonation_test.go new file mode 100644 index 000000000..5c33876ba --- /dev/null +++ b/pkg/podimpersonation/podimpersonation_test.go @@ -0,0 +1,92 @@ +package podimpersonation + +import ( + "github.com/stretchr/testify/assert" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "testing" + "time" +) + +func TestAugmentPod(t *testing.T) { + var ( + zero = int64(0) + ) + testCases := []struct { + name string + imageOverride string + envVars []v1.EnvVar + }{ + { + name: "Should mount volume to container, create an init container and use regular image", + imageOverride: "", + envVars: []v1.EnvVar{{Name: "KUBECONFIG", Value: ".kube/config"}}, + }, + { + name: "Should mount volume to container, create an init container and use overridden image", + imageOverride: "rancher/notShell:v1.0.0", + envVars: []v1.EnvVar{{Name: "KUBECONFIG", Value: ".kube/config"}}, + }, + { + name: "Should not create init container if there's no KUBECONFIG envVar", + imageOverride: "", + envVars: []v1.EnvVar{}, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + p := newPod(tc.envVars) + impersonator := New("", nil, time.Minute, func() string { return "rancher/shell:v0.1.22" }) + + pod := impersonator.augmentPod(p, nil, &v1.Secret{ObjectMeta: metav1.ObjectMeta{Name: "s"}}, tc.imageOverride) + + assert.Len(t, pod.Spec.Volumes, len(p.Spec.Volumes)+4, "expected four new volumes") + if len(tc.envVars) != 0 { + assert.Len(t, pod.Spec.Containers[0].VolumeMounts, len(p.Spec.Containers[0].VolumeMounts)+1, "expected kubeconfig volume to be mounted") + assert.Len(t, pod.Spec.InitContainers, len(p.Spec.InitContainers)+1, "expected an init container to be created") + if tc.imageOverride != "" { + assert.Equal(t, pod.Spec.InitContainers[len(pod.Spec.InitContainers)-1].Image, tc.imageOverride, "expected image to be the one received as parameter") + } else { + assert.Equal(t, pod.Spec.InitContainers[len(pod.Spec.InitContainers)-1].Image, impersonator.imageName(), "expected image to be the impersonator image") + } + assert.Equal(t, pod.Spec.InitContainers[len(pod.Spec.InitContainers)-1].SecurityContext.RunAsUser, &zero, "expected init container to run as user zero") + assert.Equal(t, pod.Spec.InitContainers[len(pod.Spec.InitContainers)-1].SecurityContext.RunAsGroup, &zero, "expected init container to run as group zero") + } else { + assert.Len(t, pod.Spec.InitContainers, len(p.Spec.InitContainers), "expected no init container to be created") + } + assert.Equal(t, pod.Spec.Containers[len(pod.Spec.Containers)-1].Name, "proxy", "expected the container proxy to be created") + }) + } +} + +func newPod(env []v1.EnvVar) *v1.Pod { + return &v1.Pod{ + Spec: v1.PodSpec{ + Volumes: []v1.Volume{{ + Name: "volume1", + VolumeSource: v1.VolumeSource{ + ConfigMap: &v1.ConfigMapVolumeSource{ + LocalObjectReference: v1.LocalObjectReference{ + Name: "cfgMap", + }, + }, + }, + }}, + Containers: []v1.Container{ + { + Name: "shell", + Image: "rancher/shell:v0.1.22", + Env: env, + VolumeMounts: []v1.VolumeMount{{ + Name: "volume1", + MountPath: "/home/vol", + }}, + }, + }, + ServiceAccountName: "svc-account-1", + AutomountServiceAccountToken: nil, + SecurityContext: nil, + }, + } +}