kube: honor pod security context IDs
If the RunAsUser, RunAsGroup, SupplementalGroups settings are not overriden in the container security context, then take the value from the pod security context. Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
This commit is contained in:
		
							parent
							
								
									82a4b8f01c
								
							
						
					
					
						commit
						9e1ee081f8
					
				|  | @ -413,21 +413,22 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY | ||||||
| 
 | 
 | ||||||
| 		specgenOpts := kube.CtrSpecGenOptions{ | 		specgenOpts := kube.CtrSpecGenOptions{ | ||||||
| 			Annotations:        annotations, | 			Annotations:        annotations, | ||||||
|  | 			ConfigMaps:         configMaps, | ||||||
| 			Container:          initCtr, | 			Container:          initCtr, | ||||||
| 			Image:              pulledImage, | 			Image:              pulledImage, | ||||||
| 			Volumes:           volumes, | 			InitContainerType:  define.AlwaysInitContainer, | ||||||
| 			PodID:             pod.ID(), | 			Labels:             labels, | ||||||
| 			PodName:           podName, |  | ||||||
| 			PodInfraID:        podInfraID, |  | ||||||
| 			ConfigMaps:        configMaps, |  | ||||||
| 			SeccompPaths:      seccompPaths, |  | ||||||
| 			RestartPolicy:     ctrRestartPolicy, |  | ||||||
| 			NetNSIsHost:       p.NetNS.IsHost(), |  | ||||||
| 			SecretsManager:    secretsManager, |  | ||||||
| 			LogDriver:          options.LogDriver, | 			LogDriver:          options.LogDriver, | ||||||
| 			LogOptions:         options.LogOptions, | 			LogOptions:         options.LogOptions, | ||||||
| 			Labels:            labels, | 			NetNSIsHost:        p.NetNS.IsHost(), | ||||||
| 			InitContainerType: define.AlwaysInitContainer, | 			PodID:              pod.ID(), | ||||||
|  | 			PodInfraID:         podInfraID, | ||||||
|  | 			PodName:            podName, | ||||||
|  | 			PodSecurityContext: podYAML.Spec.SecurityContext, | ||||||
|  | 			RestartPolicy:      ctrRestartPolicy, | ||||||
|  | 			SeccompPaths:       seccompPaths, | ||||||
|  | 			SecretsManager:     secretsManager, | ||||||
|  | 			Volumes:            volumes, | ||||||
| 		} | 		} | ||||||
| 		specGen, err := kube.ToSpecGen(ctx, &specgenOpts) | 		specGen, err := kube.ToSpecGen(ctx, &specgenOpts) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
|  | @ -461,20 +462,21 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY | ||||||
| 
 | 
 | ||||||
| 		specgenOpts := kube.CtrSpecGenOptions{ | 		specgenOpts := kube.CtrSpecGenOptions{ | ||||||
| 			Annotations:        annotations, | 			Annotations:        annotations, | ||||||
|  | 			ConfigMaps:         configMaps, | ||||||
| 			Container:          container, | 			Container:          container, | ||||||
| 			Image:              pulledImage, | 			Image:              pulledImage, | ||||||
| 			Volumes:        volumes, | 			Labels:             labels, | ||||||
| 			PodID:          pod.ID(), |  | ||||||
| 			PodName:        podName, |  | ||||||
| 			PodInfraID:     podInfraID, |  | ||||||
| 			ConfigMaps:     configMaps, |  | ||||||
| 			SeccompPaths:   seccompPaths, |  | ||||||
| 			RestartPolicy:  ctrRestartPolicy, |  | ||||||
| 			NetNSIsHost:    p.NetNS.IsHost(), |  | ||||||
| 			SecretsManager: secretsManager, |  | ||||||
| 			LogDriver:          options.LogDriver, | 			LogDriver:          options.LogDriver, | ||||||
| 			LogOptions:         options.LogOptions, | 			LogOptions:         options.LogOptions, | ||||||
| 			Labels:         labels, | 			NetNSIsHost:        p.NetNS.IsHost(), | ||||||
|  | 			PodID:              pod.ID(), | ||||||
|  | 			PodInfraID:         podInfraID, | ||||||
|  | 			PodName:            podName, | ||||||
|  | 			PodSecurityContext: podYAML.Spec.SecurityContext, | ||||||
|  | 			RestartPolicy:      ctrRestartPolicy, | ||||||
|  | 			SeccompPaths:       seccompPaths, | ||||||
|  | 			SecretsManager:     secretsManager, | ||||||
|  | 			Volumes:            volumes, | ||||||
| 		} | 		} | ||||||
| 		specGen, err := kube.ToSpecGen(ctx, &specgenOpts) | 		specGen, err := kube.ToSpecGen(ctx, &specgenOpts) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
|  |  | ||||||
|  | @ -133,6 +133,8 @@ type CtrSpecGenOptions struct { | ||||||
| 	// InitContainerType sets what type the init container is
 | 	// InitContainerType sets what type the init container is
 | ||||||
| 	// Note: When playing a kube yaml, the inti container type will be set to "always" only
 | 	// Note: When playing a kube yaml, the inti container type will be set to "always" only
 | ||||||
| 	InitContainerType string | 	InitContainerType string | ||||||
|  | 	// PodSecurityContext is the security context specified for the pod
 | ||||||
|  | 	PodSecurityContext *v1.PodSecurityContext | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGenerator, error) { | func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGenerator, error) { | ||||||
|  | @ -188,7 +190,7 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener | ||||||
| 
 | 
 | ||||||
| 	s.InitContainerType = opts.InitContainerType | 	s.InitContainerType = opts.InitContainerType | ||||||
| 
 | 
 | ||||||
| 	setupSecurityContext(s, opts.Container.SecurityContext) | 	setupSecurityContext(s, opts.Container.SecurityContext, opts.PodSecurityContext) | ||||||
| 	err := setupLivenessProbe(s, opts.Container, opts.RestartPolicy) | 	err := setupLivenessProbe(s, opts.Container, opts.RestartPolicy) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, errors.Wrap(err, "Failed to configure livenessProbe") | 		return nil, errors.Wrap(err, "Failed to configure livenessProbe") | ||||||
|  | @ -531,10 +533,14 @@ func makeHealthCheck(inCmd string, interval int32, retries int32, timeout int32, | ||||||
| 	return &hc, nil | 	return &hc, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func setupSecurityContext(s *specgen.SpecGenerator, securityContext *v1.SecurityContext) { | func setupSecurityContext(s *specgen.SpecGenerator, securityContext *v1.SecurityContext, podSecurityContext *v1.PodSecurityContext) { | ||||||
| 	if securityContext == nil { | 	if securityContext == nil { | ||||||
| 		return | 		securityContext = &v1.SecurityContext{} | ||||||
| 	} | 	} | ||||||
|  | 	if podSecurityContext == nil { | ||||||
|  | 		podSecurityContext = &v1.PodSecurityContext{} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	if securityContext.ReadOnlyRootFilesystem != nil { | 	if securityContext.ReadOnlyRootFilesystem != nil { | ||||||
| 		s.ReadOnlyFilesystem = *securityContext.ReadOnlyRootFilesystem | 		s.ReadOnlyFilesystem = *securityContext.ReadOnlyRootFilesystem | ||||||
| 	} | 	} | ||||||
|  | @ -546,7 +552,11 @@ func setupSecurityContext(s *specgen.SpecGenerator, securityContext *v1.Security | ||||||
| 		s.NoNewPrivileges = !*securityContext.AllowPrivilegeEscalation | 		s.NoNewPrivileges = !*securityContext.AllowPrivilegeEscalation | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if seopt := securityContext.SELinuxOptions; seopt != nil { | 	seopt := securityContext.SELinuxOptions | ||||||
|  | 	if seopt == nil { | ||||||
|  | 		seopt = podSecurityContext.SELinuxOptions | ||||||
|  | 	} | ||||||
|  | 	if seopt != nil { | ||||||
| 		if seopt.User != "" { | 		if seopt.User != "" { | ||||||
| 			s.SelinuxOpts = append(s.SelinuxOpts, fmt.Sprintf("user:%s", seopt.User)) | 			s.SelinuxOpts = append(s.SelinuxOpts, fmt.Sprintf("user:%s", seopt.User)) | ||||||
| 		} | 		} | ||||||
|  | @ -568,14 +578,26 @@ func setupSecurityContext(s *specgen.SpecGenerator, securityContext *v1.Security | ||||||
| 			s.CapDrop = append(s.CapDrop, string(capability)) | 			s.CapDrop = append(s.CapDrop, string(capability)) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	if securityContext.RunAsUser != nil { | 	runAsUser := securityContext.RunAsUser | ||||||
| 		s.User = fmt.Sprintf("%d", *securityContext.RunAsUser) | 	if runAsUser == nil { | ||||||
|  | 		runAsUser = podSecurityContext.RunAsUser | ||||||
| 	} | 	} | ||||||
| 	if securityContext.RunAsGroup != nil { | 	if runAsUser != nil { | ||||||
|  | 		s.User = fmt.Sprintf("%d", *runAsUser) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	runAsGroup := securityContext.RunAsGroup | ||||||
|  | 	if runAsGroup == nil { | ||||||
|  | 		runAsGroup = podSecurityContext.RunAsGroup | ||||||
|  | 	} | ||||||
|  | 	if runAsGroup != nil { | ||||||
| 		if s.User == "" { | 		if s.User == "" { | ||||||
| 			s.User = "0" | 			s.User = "0" | ||||||
| 		} | 		} | ||||||
| 		s.User = fmt.Sprintf("%s:%d", s.User, *securityContext.RunAsGroup) | 		s.User = fmt.Sprintf("%s:%d", s.User, *runAsGroup) | ||||||
|  | 	} | ||||||
|  | 	for _, group := range podSecurityContext.SupplementalGroups { | ||||||
|  | 		s.Groups = append(s.Groups, fmt.Sprintf("%d", group)) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -326,6 +326,11 @@ spec: | ||||||
|     image: {{ .Image }} |     image: {{ .Image }} | ||||||
|     name: {{ .Name }} |     name: {{ .Name }} | ||||||
|   {{ end }} |   {{ end }} | ||||||
|  | {{ end }} | ||||||
|  | {{ if .SecurityContext }} | ||||||
|  |   securityContext: | ||||||
|  |     {{ if .RunAsUser }}runAsUser: {{ .RunAsUser }}{{- end }} | ||||||
|  |     {{ if .RunAsGroup }}runAsGroup: {{ .RunAsGroup }}{{- end }} | ||||||
| {{ end }} | {{ end }} | ||||||
|   containers: |   containers: | ||||||
| {{ with .Ctrs }} | {{ with .Ctrs }} | ||||||
|  | @ -393,6 +398,8 @@ spec: | ||||||
|     {{- end }} |     {{- end }} | ||||||
|     {{ if .SecurityContext }} |     {{ if .SecurityContext }} | ||||||
|     securityContext: |     securityContext: | ||||||
|  |       {{ if .RunAsUser }}runAsUser: {{ .RunAsUser }}{{- end }} | ||||||
|  |       {{ if .RunAsGroup }}runAsGroup: {{ .RunAsGroup }}{{- end }} | ||||||
|       allowPrivilegeEscalation: true |       allowPrivilegeEscalation: true | ||||||
|       {{ if .Caps }} |       {{ if .Caps }} | ||||||
|       capabilities: |       capabilities: | ||||||
|  | @ -768,6 +775,9 @@ type Pod struct { | ||||||
| 	Volumes         []*Volume | 	Volumes         []*Volume | ||||||
| 	Labels          map[string]string | 	Labels          map[string]string | ||||||
| 	Annotations     map[string]string | 	Annotations     map[string]string | ||||||
|  | 	SecurityContext bool | ||||||
|  | 	RunAsUser       string | ||||||
|  | 	RunAsGroup      string | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type HostAlias struct { | type HostAlias struct { | ||||||
|  | @ -802,6 +812,24 @@ func getPod(options ...podOption) *Pod { | ||||||
| 
 | 
 | ||||||
| type podOption func(*Pod) | type podOption func(*Pod) | ||||||
| 
 | 
 | ||||||
|  | func withPodSecurityContext(sc bool) podOption { | ||||||
|  | 	return func(p *Pod) { | ||||||
|  | 		p.SecurityContext = sc | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func withPodRunAsUser(runAsUser string) podOption { | ||||||
|  | 	return func(p *Pod) { | ||||||
|  | 		p.RunAsUser = runAsUser | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func withPodRunAsGroup(runAsGroup string) podOption { | ||||||
|  | 	return func(p *Pod) { | ||||||
|  | 		p.RunAsGroup = runAsGroup | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func withPodName(name string) podOption { | func withPodName(name string) podOption { | ||||||
| 	return func(pod *Pod) { | 	return func(pod *Pod) { | ||||||
| 		pod.Name = name | 		pod.Name = name | ||||||
|  | @ -949,6 +977,8 @@ type Ctr struct { | ||||||
| 	Env             []Env | 	Env             []Env | ||||||
| 	EnvFrom         []EnvFrom | 	EnvFrom         []EnvFrom | ||||||
| 	InitCtrType     string | 	InitCtrType     string | ||||||
|  | 	RunAsUser       string | ||||||
|  | 	RunAsGroup      string | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // getCtr takes a list of ctrOptions and returns a Ctr with sane defaults
 | // getCtr takes a list of ctrOptions and returns a Ctr with sane defaults
 | ||||||
|  | @ -1042,6 +1072,18 @@ func withSecurityContext(sc bool) ctrOption { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func withRunAsUser(runAsUser string) ctrOption { | ||||||
|  | 	return func(c *Ctr) { | ||||||
|  | 		c.RunAsUser = runAsUser | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func withRunAsGroup(runAsGroup string) ctrOption { | ||||||
|  | 	return func(c *Ctr) { | ||||||
|  | 		c.RunAsGroup = runAsGroup | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func withCapAdd(caps []string) ctrOption { | func withCapAdd(caps []string) ctrOption { | ||||||
| 	return func(c *Ctr) { | 	return func(c *Ctr) { | ||||||
| 		c.CapAdd = caps | 		c.CapAdd = caps | ||||||
|  | @ -1105,8 +1147,12 @@ func withEnvFrom(name, from string, optional bool) ctrOption { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func makeCtrNameInPod(pod *Pod, containerName string) string { | ||||||
|  | 	return fmt.Sprintf("%s-%s", pod.Name, containerName) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func getCtrNameInPod(pod *Pod) string { | func getCtrNameInPod(pod *Pod) string { | ||||||
| 	return fmt.Sprintf("%s-%s", pod.Name, defaultCtrName) | 	return makeCtrNameInPod(pod, defaultCtrName) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type HostPath struct { | type HostPath struct { | ||||||
|  | @ -3222,6 +3268,38 @@ invalid kube kind | ||||||
| 		Expect(ls.OutputToStringArray()).To(HaveLen(1)) | 		Expect(ls.OutputToStringArray()).To(HaveLen(1)) | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
|  | 	It("podman play kube RunAsUser", func() { | ||||||
|  | 		ctr1Name := "ctr1" | ||||||
|  | 		ctr2Name := "ctr2" | ||||||
|  | 		ctr1 := getCtr(withName(ctr1Name), withSecurityContext(true), withRunAsUser("101"), withRunAsGroup("102")) | ||||||
|  | 		ctr2 := getCtr(withName(ctr2Name), withSecurityContext(true)) | ||||||
|  | 
 | ||||||
|  | 		pod := getPod( | ||||||
|  | 			withCtr(ctr1), | ||||||
|  | 			withCtr(ctr2), | ||||||
|  | 			withPodSecurityContext(true), | ||||||
|  | 			withPodRunAsUser("103"), | ||||||
|  | 			withPodRunAsGroup("104"), | ||||||
|  | 		) | ||||||
|  | 
 | ||||||
|  | 		err := generateKubeYaml("pod", pod, kubeYaml) | ||||||
|  | 		Expect(err).To(BeNil()) | ||||||
|  | 
 | ||||||
|  | 		cmd := podmanTest.Podman([]string{"play", "kube", kubeYaml}) | ||||||
|  | 		cmd.WaitWithDefaultTimeout() | ||||||
|  | 		Expect(cmd).Should(Exit(0)) | ||||||
|  | 
 | ||||||
|  | 		// we expect the user:group as configured for the container
 | ||||||
|  | 		inspect := podmanTest.Podman([]string{"container", "inspect", "--format", "'{{.Config.User}}'", makeCtrNameInPod(pod, ctr1Name)}) | ||||||
|  | 		inspect.WaitWithDefaultTimeout() | ||||||
|  | 		Expect(inspect.OutputToString()).To(Equal("'101:102'")) | ||||||
|  | 
 | ||||||
|  | 		// we expect the user:group as configured for the pod
 | ||||||
|  | 		inspect = podmanTest.Podman([]string{"container", "inspect", "--format", "'{{.Config.User}}'", makeCtrNameInPod(pod, ctr2Name)}) | ||||||
|  | 		inspect.WaitWithDefaultTimeout() | ||||||
|  | 		Expect(inspect.OutputToString()).To(Equal("'103:104'")) | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
| 	Describe("verify environment variables", func() { | 	Describe("verify environment variables", func() { | ||||||
| 		var maxLength int | 		var maxLength int | ||||||
| 		BeforeEach(func() { | 		BeforeEach(func() { | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue