Skip to content

Commit

Permalink
kube: honor pod security context IDs
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
giuseppe committed May 10, 2022
1 parent 82a4b8f commit 9e1ee08
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 50 deletions.
64 changes: 33 additions & 31 deletions pkg/domain/infra/abi/play.go
Original file line number Diff line number Diff line change
Expand Up @@ -412,22 +412,23 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
}

specgenOpts := kube.CtrSpecGenOptions{
Annotations: annotations,
Container: initCtr,
Image: pulledImage,
Volumes: volumes,
PodID: pod.ID(),
PodName: podName,
PodInfraID: podInfraID,
ConfigMaps: configMaps,
SeccompPaths: seccompPaths,
RestartPolicy: ctrRestartPolicy,
NetNSIsHost: p.NetNS.IsHost(),
SecretsManager: secretsManager,
LogDriver: options.LogDriver,
LogOptions: options.LogOptions,
Labels: labels,
InitContainerType: define.AlwaysInitContainer,
Annotations: annotations,
ConfigMaps: configMaps,
Container: initCtr,
Image: pulledImage,
InitContainerType: define.AlwaysInitContainer,
Labels: labels,
LogDriver: options.LogDriver,
LogOptions: options.LogOptions,
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)
if err != nil {
Expand Down Expand Up @@ -460,21 +461,22 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
}

specgenOpts := kube.CtrSpecGenOptions{
Annotations: annotations,
Container: container,
Image: pulledImage,
Volumes: volumes,
PodID: pod.ID(),
PodName: podName,
PodInfraID: podInfraID,
ConfigMaps: configMaps,
SeccompPaths: seccompPaths,
RestartPolicy: ctrRestartPolicy,
NetNSIsHost: p.NetNS.IsHost(),
SecretsManager: secretsManager,
LogDriver: options.LogDriver,
LogOptions: options.LogOptions,
Labels: labels,
Annotations: annotations,
ConfigMaps: configMaps,
Container: container,
Image: pulledImage,
Labels: labels,
LogDriver: options.LogDriver,
LogOptions: options.LogOptions,
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)
if err != nil {
Expand Down
38 changes: 30 additions & 8 deletions pkg/specgen/generate/kube/kube.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,8 @@ type CtrSpecGenOptions struct {
// InitContainerType sets what type the init container is
// Note: When playing a kube yaml, the inti container type will be set to "always" only
InitContainerType string
// PodSecurityContext is the security context specified for the pod
PodSecurityContext *v1.PodSecurityContext
}

func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGenerator, error) {
Expand Down Expand Up @@ -188,7 +190,7 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener

s.InitContainerType = opts.InitContainerType

setupSecurityContext(s, opts.Container.SecurityContext)
setupSecurityContext(s, opts.Container.SecurityContext, opts.PodSecurityContext)
err := setupLivenessProbe(s, opts.Container, opts.RestartPolicy)
if err != nil {
return nil, errors.Wrap(err, "Failed to configure livenessProbe")
Expand Down Expand Up @@ -531,10 +533,14 @@ func makeHealthCheck(inCmd string, interval int32, retries int32, timeout int32,
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 {
return
securityContext = &v1.SecurityContext{}
}
if podSecurityContext == nil {
podSecurityContext = &v1.PodSecurityContext{}
}

if securityContext.ReadOnlyRootFilesystem != nil {
s.ReadOnlyFilesystem = *securityContext.ReadOnlyRootFilesystem
}
Expand All @@ -546,7 +552,11 @@ func setupSecurityContext(s *specgen.SpecGenerator, securityContext *v1.Security
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 != "" {
s.SelinuxOpts = append(s.SelinuxOpts, fmt.Sprintf("user:%s", seopt.User))
}
Expand All @@ -568,14 +578,26 @@ func setupSecurityContext(s *specgen.SpecGenerator, securityContext *v1.Security
s.CapDrop = append(s.CapDrop, string(capability))
}
}
if securityContext.RunAsUser != nil {
s.User = fmt.Sprintf("%d", *securityContext.RunAsUser)
runAsUser := securityContext.RunAsUser
if runAsUser == nil {
runAsUser = podSecurityContext.RunAsUser
}
if runAsUser != nil {
s.User = fmt.Sprintf("%d", *runAsUser)
}
if securityContext.RunAsGroup != nil {

runAsGroup := securityContext.RunAsGroup
if runAsGroup == nil {
runAsGroup = podSecurityContext.RunAsGroup
}
if runAsGroup != nil {
if s.User == "" {
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))
}
}

Expand Down
100 changes: 89 additions & 11 deletions test/e2e/play_kube_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,11 @@ spec:
image: {{ .Image }}
name: {{ .Name }}
{{ end }}
{{ end }}
{{ if .SecurityContext }}
securityContext:
{{ if .RunAsUser }}runAsUser: {{ .RunAsUser }}{{- end }}
{{ if .RunAsGroup }}runAsGroup: {{ .RunAsGroup }}{{- end }}
{{ end }}
containers:
{{ with .Ctrs }}
Expand Down Expand Up @@ -393,6 +398,8 @@ spec:
{{- end }}
{{ if .SecurityContext }}
securityContext:
{{ if .RunAsUser }}runAsUser: {{ .RunAsUser }}{{- end }}
{{ if .RunAsGroup }}runAsGroup: {{ .RunAsGroup }}{{- end }}
allowPrivilegeEscalation: true
{{ if .Caps }}
capabilities:
Expand Down Expand Up @@ -758,16 +765,19 @@ func withPVCAnnotations(k, v string) pvcOption {

// Pod describes the options a kube yaml can be configured at pod level
type Pod struct {
Name string
RestartPolicy string
Hostname string
HostNetwork bool
HostAliases []HostAlias
Ctrs []*Ctr
InitCtrs []*Ctr
Volumes []*Volume
Labels map[string]string
Annotations map[string]string
Name string
RestartPolicy string
Hostname string
HostNetwork bool
HostAliases []HostAlias
Ctrs []*Ctr
InitCtrs []*Ctr
Volumes []*Volume
Labels map[string]string
Annotations map[string]string
SecurityContext bool
RunAsUser string
RunAsGroup string
}

type HostAlias struct {
Expand Down Expand Up @@ -802,6 +812,24 @@ func getPod(options ...podOption) *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 {
return func(pod *Pod) {
pod.Name = name
Expand Down Expand Up @@ -949,6 +977,8 @@ type Ctr struct {
Env []Env
EnvFrom []EnvFrom
InitCtrType string
RunAsUser string
RunAsGroup string
}

// getCtr takes a list of ctrOptions and returns a Ctr with sane defaults
Expand Down Expand Up @@ -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 {
return func(c *Ctr) {
c.CapAdd = caps
Expand Down Expand Up @@ -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 {
return fmt.Sprintf("%s-%s", pod.Name, defaultCtrName)
return makeCtrNameInPod(pod, defaultCtrName)
}

type HostPath struct {
Expand Down Expand Up @@ -3222,6 +3268,38 @@ invalid kube kind
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() {
var maxLength int
BeforeEach(func() {
Expand Down

0 comments on commit 9e1ee08

Please sign in to comment.