Generate Kube should not print default structs

If podman uses Workdir="/" or the workdir specified in the image, it
should not add it to the yaml.
If Podman find environment variables in the image, they should not
get added to the yaml.

If the container or pod do not have changes to SELinux we should not
print seLinuxOpt{}

If the container or pod do not change any dns options the yaml should
not have a dnsOption={}

If the container is not privileged it should not have privileged=false
in the yaml.

Fixes: https://github.com/containers/podman/issues/11995

Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
This commit is contained in:
Daniel J Walsh
2021-10-18 11:54:44 -04:00
parent e0ffc431fe
commit 517b56b02d
3 changed files with 63 additions and 82 deletions

View File

@ -54,13 +54,7 @@ spec:
- docker-entrypoint.sh - docker-entrypoint.sh
- mysqld - mysqld
env: env:
- name: PATH
value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
- name: TERM
value: xterm
- name: HOSTNAME - name: HOSTNAME
- name: container
value: podman
- name: GOSU_VERSION - name: GOSU_VERSION
value: "1.10" value: "1.10"
- name: GPG_KEYS - name: GPG_KEYS
@ -77,14 +71,14 @@ spec:
ports: ports:
- containerPort: 3306 - containerPort: 3306
hostPort: 36533 hostPort: 36533
protocol: TCP
resources: {} resources: {}
securityContext: securityContext:
allowPrivilegeEscalation: true capabilities:
privileged: false drop:
readOnlyRootFilesystem: false - CAP_MKNOD
- CAP_NET_RAW
- CAP_AUDIT_WRITE
tty: true tty: true
workingDir: /
status: {} status: {}
``` ```
@ -106,31 +100,18 @@ spec:
containers: containers:
- command: - command:
- /bin/sh - /bin/sh
env:
- name: PATH
value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
- name: TERM
value: xterm
- name: container
value: podman
image: docker.io/library/alpine:latest image: docker.io/library/alpine:latest
name: test-bind-mount name: test-bind-mount
resources: {} resources: {}
securityContext: securityContext:
allowPrivilegeEscalation: true
capabilities: capabilities:
drop: drop:
- CAP_MKNOD - CAP_MKNOD
- CAP_NET_RAW - CAP_NET_RAW
- CAP_AUDIT_WRITE - CAP_AUDIT_WRITE
privileged: false
readOnlyRootFilesystem: false
seLinuxOptions: {}
volumeMounts: volumeMounts:
- mountPath: /volume - mountPath: /volume
name: home-user-my-data-host name: home-user-my-data-host
workingDir: /
dnsConfig: {}
restartPolicy: Never restartPolicy: Never
volumes: volumes:
- hostPath: - hostPath:
@ -158,31 +139,18 @@ spec:
containers: containers:
- command: - command:
- /bin/sh - /bin/sh
env:
- name: PATH
value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
- name: TERM
value: xterm
- name: container
value: podman
image: docker.io/library/alpine:latest image: docker.io/library/alpine:latest
name: test-bind-mount name: test-bind-mount
resources: {} resources: {}
securityContext: securityContext:
allowPrivilegeEscalation: true
capabilities: capabilities:
drop: drop:
- CAP_MKNOD - CAP_MKNOD
- CAP_NET_RAW - CAP_NET_RAW
- CAP_AUDIT_WRITE - CAP_AUDIT_WRITE
privileged: false
readOnlyRootFilesystem: false
seLinuxOptions: {}
volumeMounts: volumeMounts:
- mountPath: /volume - mountPath: /volume
name: priceless-data-pvc name: priceless-data-pvc
workingDir: /
dnsConfig: {}
restartPolicy: Never restartPolicy: Never
volumes: volumes:
- name: priceless-data-pvc - name: priceless-data-pvc
@ -210,22 +178,9 @@ spec:
- command: - command:
- python3 - python3
- /root/code/graph.py - /root/code/graph.py
env:
- name: PATH
value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
- name: TERM
value: xterm
- name: HOSTNAME
- name: container
value: podman
image: quay.io/baude/demoweb:latest image: quay.io/baude/demoweb:latest
name: practicalarchimedes name: practicalarchimedes
resources: {} resources: {}
securityContext:
allowPrivilegeEscalation: true
capabilities: {}
privileged: false
readOnlyRootFilesystem: false
tty: true tty: true
workingDir: /root/code workingDir: /root/code
status: {} status: {}
@ -242,7 +197,6 @@ spec:
- name: "8050" - name: "8050"
nodePort: 31269 nodePort: 31269
port: 8050 port: 8050
protocol: TCP
targetPort: 0 targetPort: 0
selector: selector:
app: demoweb app: demoweb

View File

@ -332,7 +332,7 @@ func newPodObject(podName string, annotations map[string]string, initCtrs, conta
InitContainers: initCtrs, InitContainers: initCtrs,
Volumes: volumes, Volumes: volumes,
} }
if dnsOptions != nil { if dnsOptions != nil && (len(dnsOptions.Nameservers)+len(dnsOptions.Searches)+len(dnsOptions.Options) > 0) {
ps.DNSConfig = dnsOptions ps.DNSConfig = dnsOptions
} }
p := v1.Pod{ p := v1.Pod{
@ -447,11 +447,6 @@ func containerToV1Container(ctx context.Context, c *Container) (v1.Container, []
kubeVolumes = append(kubeVolumes, volumes...) kubeVolumes = append(kubeVolumes, volumes...)
} }
envVariables, err := libpodEnvVarsToKubeEnvVars(c.config.Spec.Process.Env)
if err != nil {
return kubeContainer, kubeVolumes, nil, annotations, err
}
portmappings, err := c.PortMappings() portmappings, err := c.PortMappings()
if err != nil { if err != nil {
return kubeContainer, kubeVolumes, nil, annotations, err return kubeContainer, kubeVolumes, nil, annotations, err
@ -489,15 +484,23 @@ func containerToV1Container(ctx context.Context, c *Container) (v1.Container, []
kubeContainer.Command = nil kubeContainer.Command = nil
} }
if c.WorkingDir() != "/" && imgData.Config.WorkingDir != c.WorkingDir() {
kubeContainer.WorkingDir = c.WorkingDir()
}
if imgData.User == c.User() { if imgData.User == c.User() {
kubeSec.RunAsGroup, kubeSec.RunAsUser = nil, nil kubeSec.RunAsGroup, kubeSec.RunAsUser = nil, nil
} }
kubeContainer.WorkingDir = c.WorkingDir() envVariables, err := libpodEnvVarsToKubeEnvVars(c.config.Spec.Process.Env, imgData.Config.Env)
if err != nil {
return kubeContainer, kubeVolumes, nil, annotations, err
}
kubeContainer.Env = envVariables
kubeContainer.Ports = ports kubeContainer.Ports = ports
// This should not be applicable // This should not be applicable
//container.EnvFromSource = //container.EnvFromSource =
kubeContainer.Env = envVariables
kubeContainer.SecurityContext = kubeSec kubeContainer.SecurityContext = kubeSec
kubeContainer.StdinOnce = false kubeContainer.StdinOnce = false
kubeContainer.TTY = c.config.Spec.Process.Terminal kubeContainer.TTY = c.config.Spec.Process.Terminal
@ -600,9 +603,14 @@ func ocicniPortMappingToContainerPort(portMappings []types.OCICNIPortMapping) ([
} }
// libpodEnvVarsToKubeEnvVars converts a key=value string slice to []v1.EnvVar // libpodEnvVarsToKubeEnvVars converts a key=value string slice to []v1.EnvVar
func libpodEnvVarsToKubeEnvVars(envs []string) ([]v1.EnvVar, error) { func libpodEnvVarsToKubeEnvVars(envs []string, imageEnvs []string) ([]v1.EnvVar, error) {
defaultEnv := env.DefaultEnvVariables() defaultEnv := env.DefaultEnvVariables()
envVars := make([]v1.EnvVar, 0, len(envs)) envVars := make([]v1.EnvVar, 0, len(envs))
imageMap := make(map[string]string, len(imageEnvs))
for _, ie := range envs {
split := strings.SplitN(ie, "=", 2)
imageMap[split[0]] = split[1]
}
for _, e := range envs { for _, e := range envs {
split := strings.SplitN(e, "=", 2) split := strings.SplitN(e, "=", 2)
if len(split) != 2 { if len(split) != 2 {
@ -611,6 +619,9 @@ func libpodEnvVarsToKubeEnvVars(envs []string) ([]v1.EnvVar, error) {
if defaultEnv[split[0]] == split[1] { if defaultEnv[split[0]] == split[1] {
continue continue
} }
if imageMap[split[0]] == split[1] {
continue
}
ev := v1.EnvVar{ ev := v1.EnvVar{
Name: split[0], Name: split[0],
Value: split[1], Value: split[1],
@ -808,33 +819,42 @@ func generateKubeSecurityContext(c *Container) (*v1.SecurityContext, error) {
capabilities = newCaps capabilities = newCaps
} }
var selinuxOpts v1.SELinuxOptions
opts := strings.SplitN(c.config.Spec.Annotations[define.InspectAnnotationLabel], ":", 2)
if len(opts) == 2 {
switch opts[0] {
case "type":
selinuxOpts.Type = opts[1]
case "level":
selinuxOpts.Level = opts[1]
}
}
if len(opts) == 1 {
if opts[0] == "disable" {
selinuxOpts.Type = "spc_t"
}
}
sc := v1.SecurityContext{ sc := v1.SecurityContext{
Capabilities: capabilities,
Privileged: &privileged,
SELinuxOptions: &selinuxOpts,
// RunAsNonRoot is an optional parameter; our first implementations should be root only; however // RunAsNonRoot is an optional parameter; our first implementations should be root only; however
// I'm leaving this as a bread-crumb for later // I'm leaving this as a bread-crumb for later
//RunAsNonRoot: &nonRoot, //RunAsNonRoot: &nonRoot,
ReadOnlyRootFilesystem: &ro, }
AllowPrivilegeEscalation: &allowPrivEscalation, if capabilities != nil {
sc.Capabilities = capabilities
}
var selinuxOpts v1.SELinuxOptions
opts := strings.SplitN(c.config.Spec.Annotations[define.InspectAnnotationLabel], ":", 2)
switch len(opts) {
case 2:
switch opts[0] {
case "type":
selinuxOpts.Type = opts[1]
sc.SELinuxOptions = &selinuxOpts
case "level":
selinuxOpts.Level = opts[1]
sc.SELinuxOptions = &selinuxOpts
}
case 1:
if opts[0] == "disable" {
selinuxOpts.Type = "spc_t"
sc.SELinuxOptions = &selinuxOpts
}
} }
if !allowPrivEscalation {
sc.AllowPrivilegeEscalation = &allowPrivEscalation
}
if privileged {
sc.Privileged = &privileged
}
if ro {
sc.ReadOnlyRootFilesystem = &ro
}
if c.User() != "" { if c.User() != "" {
if !c.batched { if !c.batched {
c.lock.Lock() c.lock.Lock()

View File

@ -67,6 +67,10 @@ var _ = Describe("Podman generate kube", func() {
err := yaml.Unmarshal(kube.Out.Contents(), pod) err := yaml.Unmarshal(kube.Out.Contents(), pod)
Expect(err).To(BeNil()) Expect(err).To(BeNil())
Expect(pod.Spec.HostNetwork).To(Equal(false)) Expect(pod.Spec.HostNetwork).To(Equal(false))
Expect(pod.Spec.SecurityContext).To(BeNil())
Expect(pod.Spec.DNSConfig).To(BeNil())
Expect(pod.Spec.Containers[0].WorkingDir).To(Equal(""))
Expect(pod.Spec.Containers[0].Env).To(BeNil())
numContainers := 0 numContainers := 0
for range pod.Spec.Containers { for range pod.Spec.Containers {
@ -103,6 +107,7 @@ var _ = Describe("Podman generate kube", func() {
err = yaml.Unmarshal(kube.Out.Contents(), pod) err = yaml.Unmarshal(kube.Out.Contents(), pod)
Expect(err).To(BeNil()) Expect(err).To(BeNil())
Expect(kube.OutputToString()).To(ContainSubstring("type: spc_t")) Expect(kube.OutputToString()).To(ContainSubstring("type: spc_t"))
}) })
It("podman generate service kube on container with --security-opt type", func() { It("podman generate service kube on container with --security-opt type", func() {
@ -1079,7 +1084,7 @@ USER test1`
top1.WaitWithDefaultTimeout() top1.WaitWithDefaultTimeout()
Expect(top1).Should(Exit(0)) Expect(top1).Should(Exit(0))
top2 := podmanTest.Podman([]string{"run", "-dt", "--name", "top2", "--pod", "pod1", "--label", "io.containers.autoupdate=registry", "--label", "io.containers.autoupdate.authfile=/some/authfile.json", ALPINE, "top"}) top2 := podmanTest.Podman([]string{"run", "-dt", "--name", "top2", "--workdir", "/root", "--pod", "pod1", "--label", "io.containers.autoupdate=registry", "--label", "io.containers.autoupdate.authfile=/some/authfile.json", ALPINE, "top"})
top2.WaitWithDefaultTimeout() top2.WaitWithDefaultTimeout()
Expect(top2).Should(Exit(0)) Expect(top2).Should(Exit(0))
@ -1090,6 +1095,8 @@ USER test1`
pod := new(v1.Pod) pod := new(v1.Pod)
err := yaml.Unmarshal(kube.Out.Contents(), pod) err := yaml.Unmarshal(kube.Out.Contents(), pod)
Expect(err).To(BeNil()) Expect(err).To(BeNil())
Expect(pod.Spec.Containers[0].WorkingDir).To(Equal(""))
Expect(pod.Spec.Containers[1].WorkingDir).To(Equal("/root"))
for _, ctr := range []string{"top1", "top2"} { for _, ctr := range []string{"top1", "top2"} {
v, ok := pod.GetAnnotations()["io.containers.autoupdate/"+ctr] v, ok := pod.GetAnnotations()["io.containers.autoupdate/"+ctr]