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
3 changed files with 152 additions and 50 deletions

View File

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

View File

@ -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) {
@ -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")
@ -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
}
@ -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))
}
@ -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 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 == "" {
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 }}
name: {{ .Name }}
{{ end }}
{{ end }}
{{ if .SecurityContext }}
securityContext:
{{ if .RunAsUser }}runAsUser: {{ .RunAsUser }}{{- end }}
{{ if .RunAsGroup }}runAsGroup: {{ .RunAsGroup }}{{- end }}
{{ end }}
containers:
{{ with .Ctrs }}
@ -393,6 +398,8 @@ spec:
{{- end }}
{{ if .SecurityContext }}
securityContext:
{{ if .RunAsUser }}runAsUser: {{ .RunAsUser }}{{- end }}
{{ if .RunAsGroup }}runAsGroup: {{ .RunAsGroup }}{{- end }}
allowPrivilegeEscalation: true
{{ if .Caps }}
capabilities:
@ -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 {
@ -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
@ -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
@ -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
@ -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 {
@ -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() {