mirror of
https://github.com/containers/podman.git
synced 2025-06-16 14:46:39 +08:00
Merge pull request #16682 from rhatdan/ro
Fix handling of readonly containers when defined in kube.yaml
This commit is contained in:
libpod
pkg
test
@ -435,7 +435,7 @@ func (p *Pod) podWithContainers(ctx context.Context, containers []*Container, po
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
podVolumes := make([]v1.Volume, 0, len(deDupPodVolumes))
|
podVolumes := []v1.Volume{}
|
||||||
for _, vol := range deDupPodVolumes {
|
for _, vol := range deDupPodVolumes {
|
||||||
podVolumes = append(podVolumes, *vol)
|
podVolumes = append(podVolumes, *vol)
|
||||||
}
|
}
|
||||||
@ -471,18 +471,12 @@ func newPodObject(podName string, annotations map[string]string, initCtrs, conta
|
|||||||
CreationTimestamp: v12.Now(),
|
CreationTimestamp: v12.Now(),
|
||||||
Annotations: annotations,
|
Annotations: annotations,
|
||||||
}
|
}
|
||||||
// Set enableServiceLinks to false as podman doesn't use the service port environment variables
|
|
||||||
enableServiceLinks := false
|
|
||||||
// Set automountServiceAccountToken to false as podman doesn't use service account tokens
|
|
||||||
automountServiceAccountToken := false
|
|
||||||
ps := v1.PodSpec{
|
ps := v1.PodSpec{
|
||||||
Containers: containers,
|
Containers: containers,
|
||||||
Hostname: hostname,
|
Hostname: hostname,
|
||||||
HostNetwork: hostNetwork,
|
HostNetwork: hostNetwork,
|
||||||
InitContainers: initCtrs,
|
InitContainers: initCtrs,
|
||||||
Volumes: volumes,
|
Volumes: volumes,
|
||||||
EnableServiceLinks: &enableServiceLinks,
|
|
||||||
AutomountServiceAccountToken: &automountServiceAccountToken,
|
|
||||||
}
|
}
|
||||||
if !hostUsers {
|
if !hostUsers {
|
||||||
ps.HostUsers = &hostUsers
|
ps.HostUsers = &hostUsers
|
||||||
@ -894,35 +888,46 @@ func generateKubeVolumeMount(m specs.Mount) (v1.VolumeMount, v1.Volume, error) {
|
|||||||
vm := v1.VolumeMount{}
|
vm := v1.VolumeMount{}
|
||||||
vo := v1.Volume{}
|
vo := v1.Volume{}
|
||||||
|
|
||||||
name, err := convertVolumePathToName(m.Source)
|
var (
|
||||||
if err != nil {
|
name string
|
||||||
return vm, vo, err
|
err error
|
||||||
|
)
|
||||||
|
if m.Type == define.TypeTmpfs {
|
||||||
|
name = "tmp"
|
||||||
|
vo.EmptyDir = &v1.EmptyDirVolumeSource{
|
||||||
|
Medium: v1.StorageMediumMemory,
|
||||||
|
}
|
||||||
|
vo.Name = name
|
||||||
|
} else {
|
||||||
|
name, err = convertVolumePathToName(m.Source)
|
||||||
|
if err != nil {
|
||||||
|
return vm, vo, err
|
||||||
|
}
|
||||||
|
// To avoid naming conflicts with any persistent volume mounts, add a unique suffix to the volume's name.
|
||||||
|
name += "-host"
|
||||||
|
vo.Name = name
|
||||||
|
vo.HostPath = &v1.HostPathVolumeSource{}
|
||||||
|
vo.HostPath.Path = m.Source
|
||||||
|
isDir, err := isHostPathDirectory(m.Source)
|
||||||
|
// neither a directory or a file lives here, default to creating a directory
|
||||||
|
// TODO should this be an error instead?
|
||||||
|
var hostPathType v1.HostPathType
|
||||||
|
switch {
|
||||||
|
case err != nil:
|
||||||
|
hostPathType = v1.HostPathDirectoryOrCreate
|
||||||
|
case isDir:
|
||||||
|
hostPathType = v1.HostPathDirectory
|
||||||
|
default:
|
||||||
|
hostPathType = v1.HostPathFile
|
||||||
|
}
|
||||||
|
vo.HostPath.Type = &hostPathType
|
||||||
}
|
}
|
||||||
// To avoid naming conflicts with any persistent volume mounts, add a unique suffix to the volume's name.
|
|
||||||
name += "-host"
|
|
||||||
vm.Name = name
|
vm.Name = name
|
||||||
vm.MountPath = m.Destination
|
vm.MountPath = m.Destination
|
||||||
if cutil.StringInSlice("ro", m.Options) {
|
if cutil.StringInSlice("ro", m.Options) {
|
||||||
vm.ReadOnly = true
|
vm.ReadOnly = true
|
||||||
}
|
}
|
||||||
|
|
||||||
vo.Name = name
|
|
||||||
vo.HostPath = &v1.HostPathVolumeSource{}
|
|
||||||
vo.HostPath.Path = m.Source
|
|
||||||
isDir, err := isHostPathDirectory(m.Source)
|
|
||||||
// neither a directory or a file lives here, default to creating a directory
|
|
||||||
// TODO should this be an error instead?
|
|
||||||
var hostPathType v1.HostPathType
|
|
||||||
switch {
|
|
||||||
case err != nil:
|
|
||||||
hostPathType = v1.HostPathDirectoryOrCreate
|
|
||||||
case isDir:
|
|
||||||
hostPathType = v1.HostPathDirectory
|
|
||||||
default:
|
|
||||||
hostPathType = v1.HostPathFile
|
|
||||||
}
|
|
||||||
vo.HostPath.Type = &hostPathType
|
|
||||||
|
|
||||||
return vm, vo, nil
|
return vm, vo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ import (
|
|||||||
"github.com/containers/common/pkg/secrets"
|
"github.com/containers/common/pkg/secrets"
|
||||||
cutil "github.com/containers/common/pkg/util"
|
cutil "github.com/containers/common/pkg/util"
|
||||||
"github.com/containers/image/v5/manifest"
|
"github.com/containers/image/v5/manifest"
|
||||||
|
itypes "github.com/containers/image/v5/types"
|
||||||
"github.com/containers/podman/v4/libpod/define"
|
"github.com/containers/podman/v4/libpod/define"
|
||||||
ann "github.com/containers/podman/v4/pkg/annotations"
|
ann "github.com/containers/podman/v4/pkg/annotations"
|
||||||
"github.com/containers/podman/v4/pkg/domain/entities"
|
"github.com/containers/podman/v4/pkg/domain/entities"
|
||||||
@ -119,6 +120,8 @@ type CtrSpecGenOptions struct {
|
|||||||
ConfigMaps []v1.ConfigMap
|
ConfigMaps []v1.ConfigMap
|
||||||
// SeccompPaths for finding the seccomp profile path
|
// SeccompPaths for finding the seccomp profile path
|
||||||
SeccompPaths *KubeSeccompPaths
|
SeccompPaths *KubeSeccompPaths
|
||||||
|
// ReadOnly make all containers root file system readonly
|
||||||
|
ReadOnly itypes.OptionalBool
|
||||||
// RestartPolicy defines the restart policy of the container
|
// RestartPolicy defines the restart policy of the container
|
||||||
RestartPolicy string
|
RestartPolicy string
|
||||||
// NetNSIsHost tells the container to use the host netns
|
// NetNSIsHost tells the container to use the host netns
|
||||||
@ -444,6 +447,10 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ro := opts.ReadOnly; ro != itypes.OptionalBoolUndefined {
|
||||||
|
s.ReadOnlyFilesystem = (ro == itypes.OptionalBoolTrue)
|
||||||
|
}
|
||||||
|
|
||||||
// Make sure the container runs in a systemd unit which is
|
// Make sure the container runs in a systemd unit which is
|
||||||
// stored as a label at container creation.
|
// stored as a label at container creation.
|
||||||
if unit := os.Getenv(systemdDefine.EnvVariable); unit != "" {
|
if unit := os.Getenv(systemdDefine.EnvVariable); unit != "" {
|
||||||
|
@ -676,7 +676,7 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions
|
|||||||
|
|
||||||
// Only add read-only tmpfs mounts in case that we are read-only and the
|
// Only add read-only tmpfs mounts in case that we are read-only and the
|
||||||
// read-only tmpfs flag has been set.
|
// read-only tmpfs flag has been set.
|
||||||
mounts, volumes, overlayVolumes, imageVolumes, err := parseVolumes(c.Volume, c.Mount, c.TmpFS, c.ReadOnlyTmpFS && c.ReadOnly)
|
mounts, volumes, overlayVolumes, imageVolumes, err := parseVolumes(c.Volume, c.Mount, c.TmpFS)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -854,6 +854,10 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions
|
|||||||
s.PasswdEntry = c.PasswdEntry
|
s.PasswdEntry = c.PasswdEntry
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.ReadOnly && c.ReadOnlyTmpFS {
|
||||||
|
s.Mounts = addReadOnlyMounts(s.Mounts)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ var (
|
|||||||
// Does not handle image volumes, init, and --volumes-from flags.
|
// Does not handle image volumes, init, and --volumes-from flags.
|
||||||
// Can also add tmpfs mounts from read-only tmpfs.
|
// Can also add tmpfs mounts from read-only tmpfs.
|
||||||
// TODO: handle options parsing/processing via containers/storage/pkg/mount
|
// TODO: handle options parsing/processing via containers/storage/pkg/mount
|
||||||
func parseVolumes(volumeFlag, mountFlag, tmpfsFlag []string, addReadOnlyTmpfs bool) ([]spec.Mount, []*specgen.NamedVolume, []*specgen.OverlayVolume, []*specgen.ImageVolume, error) {
|
func parseVolumes(volumeFlag, mountFlag, tmpfsFlag []string) ([]spec.Mount, []*specgen.NamedVolume, []*specgen.OverlayVolume, []*specgen.ImageVolume, error) {
|
||||||
// Get mounts from the --mounts flag.
|
// Get mounts from the --mounts flag.
|
||||||
unifiedMounts, unifiedVolumes, unifiedImageVolumes, err := Mounts(mountFlag)
|
unifiedMounts, unifiedVolumes, unifiedImageVolumes, err := Mounts(mountFlag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -78,26 +78,6 @@ func parseVolumes(volumeFlag, mountFlag, tmpfsFlag []string, addReadOnlyTmpfs bo
|
|||||||
unifiedMounts[dest] = tmpfs
|
unifiedMounts[dest] = tmpfs
|
||||||
}
|
}
|
||||||
|
|
||||||
// If requested, add tmpfs filesystems for read-only containers.
|
|
||||||
if addReadOnlyTmpfs {
|
|
||||||
readonlyTmpfs := []string{"/tmp", "/var/tmp", "/run"}
|
|
||||||
options := []string{"rw", "rprivate", "nosuid", "nodev", "tmpcopyup"}
|
|
||||||
for _, dest := range readonlyTmpfs {
|
|
||||||
if _, ok := unifiedMounts[dest]; ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if _, ok := unifiedVolumes[dest]; ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
unifiedMounts[dest] = spec.Mount{
|
|
||||||
Destination: dest,
|
|
||||||
Type: define.TypeTmpfs,
|
|
||||||
Source: "tmpfs",
|
|
||||||
Options: options,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for conflicts between named volumes, overlay & image volumes,
|
// Check for conflicts between named volumes, overlay & image volumes,
|
||||||
// and mounts
|
// and mounts
|
||||||
allMounts := make(map[string]bool)
|
allMounts := make(map[string]bool)
|
||||||
@ -723,3 +703,26 @@ func validChownFlag(flag string) (bool, error) {
|
|||||||
func unixPathClean(p string) string {
|
func unixPathClean(p string) string {
|
||||||
return path.Clean(p)
|
return path.Clean(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func addReadOnlyMounts(mounts []spec.Mount) []spec.Mount {
|
||||||
|
readonlyTmpfs := []string{"/tmp", "/var/tmp", "/run"}
|
||||||
|
options := []string{"rw", "rprivate", "nosuid", "nodev", "tmpcopyup"}
|
||||||
|
for _, dest := range readonlyTmpfs {
|
||||||
|
for _, m := range mounts {
|
||||||
|
if m.Destination == dest {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mnt := spec.Mount{
|
||||||
|
Destination: dest,
|
||||||
|
Type: define.TypeTmpfs,
|
||||||
|
Source: define.TypeTmpfs,
|
||||||
|
Options: options,
|
||||||
|
}
|
||||||
|
if dest != "/run" {
|
||||||
|
mnt.Options = append(mnt.Options, "noexec")
|
||||||
|
}
|
||||||
|
mounts = append(mounts, mnt)
|
||||||
|
}
|
||||||
|
return mounts
|
||||||
|
}
|
||||||
|
@ -71,10 +71,6 @@ var _ = Describe("Podman generate kube", func() {
|
|||||||
Expect(pod.Spec.Containers[0]).To(HaveField("WorkingDir", ""))
|
Expect(pod.Spec.Containers[0]).To(HaveField("WorkingDir", ""))
|
||||||
Expect(pod.Spec.Containers[0].Env).To(BeNil())
|
Expect(pod.Spec.Containers[0].Env).To(BeNil())
|
||||||
Expect(pod).To(HaveField("Name", "top-pod"))
|
Expect(pod).To(HaveField("Name", "top-pod"))
|
||||||
enableServiceLinks := false
|
|
||||||
Expect(pod.Spec).To(HaveField("EnableServiceLinks", &enableServiceLinks))
|
|
||||||
automountServiceAccountToken := false
|
|
||||||
Expect(pod.Spec).To(HaveField("AutomountServiceAccountToken", &automountServiceAccountToken))
|
|
||||||
|
|
||||||
numContainers := 0
|
numContainers := 0
|
||||||
for range pod.Spec.Containers {
|
for range pod.Spec.Containers {
|
||||||
@ -169,10 +165,6 @@ var _ = Describe("Podman generate kube", func() {
|
|||||||
err := yaml.Unmarshal(kube.Out.Contents(), pod)
|
err := yaml.Unmarshal(kube.Out.Contents(), pod)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(pod.Spec).To(HaveField("HostNetwork", false))
|
Expect(pod.Spec).To(HaveField("HostNetwork", false))
|
||||||
enableServiceLinks := false
|
|
||||||
Expect(pod.Spec).To(HaveField("EnableServiceLinks", &enableServiceLinks))
|
|
||||||
automountServiceAccountToken := false
|
|
||||||
Expect(pod.Spec).To(HaveField("AutomountServiceAccountToken", &automountServiceAccountToken))
|
|
||||||
|
|
||||||
numContainers := 0
|
numContainers := 0
|
||||||
for range pod.Spec.Containers {
|
for range pod.Spec.Containers {
|
||||||
|
@ -196,6 +196,30 @@ EOF
|
|||||||
run_podman rm -a
|
run_podman rm -a
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@test "podman kube play read-only" {
|
||||||
|
YAML=$PODMAN_TMPDIR/test.yml
|
||||||
|
run_podman create --pod new:pod1 --name test1 $IMAGE touch /testrw
|
||||||
|
run_podman create --pod pod1 --read-only --name test2 $IMAGE touch /testro
|
||||||
|
run_podman create --pod pod1 --read-only --name test3 $IMAGE touch /tmp/testtmp
|
||||||
|
run_podman kube generate pod1 -f $YAML
|
||||||
|
|
||||||
|
run_podman kube play --replace $YAML
|
||||||
|
run_podman container inspect --format '{{.HostConfig.ReadonlyRootfs}}' pod1-test1 pod1-test2 pod1-test3
|
||||||
|
is "$output" "false.*true.*true" "Rootfs should be read/only"
|
||||||
|
|
||||||
|
run_podman inspect --format "{{.State.ExitCode}}" pod1-test1
|
||||||
|
is "$output" "0" "Container / should be read/write"
|
||||||
|
run_podman inspect --format "{{.State.ExitCode}}" pod1-test2
|
||||||
|
is "$output" "1" "Container / should be read/only"
|
||||||
|
run_podman inspect --format "{{.State.ExitCode}}" pod1-test3
|
||||||
|
is "$output" "0" "/tmp in a read-only container should be read/write"
|
||||||
|
|
||||||
|
run_podman kube down - < $YAML
|
||||||
|
run_podman 1 container exists pod1-test1
|
||||||
|
run_podman 1 container exists pod1-test2
|
||||||
|
run_podman 1 container exists pod1-test3
|
||||||
|
}
|
||||||
|
|
||||||
@test "podman play with user from image" {
|
@test "podman play with user from image" {
|
||||||
TESTDIR=$PODMAN_TMPDIR/testdir
|
TESTDIR=$PODMAN_TMPDIR/testdir
|
||||||
mkdir -p $TESTDIR
|
mkdir -p $TESTDIR
|
||||||
@ -416,4 +440,7 @@ spec:
|
|||||||
run_podman pod inspect test_pod --format "{{.InfraConfig.PortBindings}}"
|
run_podman pod inspect test_pod --format "{{.InfraConfig.PortBindings}}"
|
||||||
assert "$output" = "map[$HOST_PORT/tcp:[{ $HOST_PORT}]]"
|
assert "$output" = "map[$HOST_PORT/tcp:[{ $HOST_PORT}]]"
|
||||||
run_podman kube down $PODMAN_TMPDIR/testpod.yaml
|
run_podman kube down $PODMAN_TMPDIR/testpod.yaml
|
||||||
|
|
||||||
|
run_podman pod rm -a -f
|
||||||
|
run_podman rm -a -f
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user