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:
Giuseppe Scrivano 2022-05-09 16:37:34 +02:00
parent 82a4b8f01c
commit 9e1ee081f8
No known key found for this signature in database
GPG Key ID: 67E38F7A8BA21772
3 changed files with 152 additions and 50 deletions

View File

@ -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 {

View File

@ -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))
} }
} }

View File

@ -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() {