Add support for ramfs as well as tmpfs in volume mounts
Users want to mount a tmpfs file system with secrets, and make sure the secret is never saved into swap. They can do this either by using a ramfs tmpfs mount or by passing `noswap` option to a tmpfs mount. Fixes: https://github.com/containers/podman/issues/19659 Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
This commit is contained in:
parent
84447c0855
commit
45ce4834af
|
@ -6,25 +6,28 @@
|
|||
|
||||
Attach a filesystem mount to the container
|
||||
|
||||
Current supported mount TYPEs are **bind**, **devpts**, **glob**, **image**, **tmpfs** and **volume**. <sup>[[1]](#Footnote1)</sup>
|
||||
Current supported mount TYPEs are **bind**, **devpts**, **glob**, **image**, **ramfs**, **tmpfs** and **volume**. <sup>[[1]](#Footnote1)</sup>
|
||||
|
||||
e.g.
|
||||
|
||||
type=bind,source=/path/on/host,destination=/path/in/container
|
||||
|
||||
type=bind,src=/path/on/host,dst=/path/in/container,relabel=shared
|
||||
|
||||
type=bind,src=/path/on/host,dst=/path/in/container,relabel=shared,U=true
|
||||
|
||||
type=devpts,destination=/dev/pts
|
||||
|
||||
type=glob,src=/usr/lib/libfoo*,destination=/usr/lib,ro=true
|
||||
|
||||
type=volume,source=vol1,destination=/path/in/container,ro=true
|
||||
|
||||
type=tmpfs,tmpfs-size=512M,destination=/path/in/container
|
||||
|
||||
type=image,source=fedora,destination=/fedora-image,rw=true
|
||||
|
||||
type=devpts,destination=/dev/pts
|
||||
type=ramfs,tmpfs-size=512M,destination=/path/in/container
|
||||
|
||||
type=tmpfs,tmpfs-size=512M,destination=/path/in/container
|
||||
|
||||
type=tmpfs,destination=/path/in/container,noswap
|
||||
|
||||
type=volume,source=vol1,destination=/path/in/container,ro=true
|
||||
|
||||
Common Options:
|
||||
|
||||
|
@ -72,17 +75,17 @@ Current supported mount TYPEs are **bind**, **devpts**, **glob**, **image**, **t
|
|||
|
||||
. U, chown: true or false (default). Change recursively the owner and group of the source volume based on the UID and GID of the container.
|
||||
|
||||
Options specific to tmpfs:
|
||||
Options specific to tmpfs and ramfs:
|
||||
|
||||
· ro, readonly: true or false (default).
|
||||
|
||||
· tmpfs-size: Size of the tmpfs mount in bytes. Unlimited by default in Linux.
|
||||
· tmpfs-size: Size of the tmpfs/ramfs mount in bytes. Unlimited by default in Linux.
|
||||
|
||||
· tmpfs-mode: File mode of the tmpfs in octal. (e.g. 700 or 0700.) Defaults to 1777 in Linux.
|
||||
· tmpfs-mode: File mode of the tmpfs/ramfs in octal. (e.g. 700 or 0700.) Defaults to 1777 in Linux.
|
||||
|
||||
· tmpcopyup: Enable copyup from the image directory at the same location to the tmpfs. Used by default.
|
||||
· tmpcopyup: Enable copyup from the image directory at the same location to the tmpfs/ramfs. Used by default.
|
||||
|
||||
· notmpcopyup: Disable copying files from the image to the tmpfs.
|
||||
· notmpcopyup: Disable copying files from the image to the tmpfs/ramfs.
|
||||
|
||||
. U, chown: true or false (default). Change recursively the owner and group of the source volume based on the UID and GID of the container.
|
||||
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
package define
|
||||
|
||||
const (
|
||||
// TypeVolume is the type for named volumes
|
||||
TypeVolume = "volume"
|
||||
// TypeTmpfs is the type for mounting tmpfs
|
||||
TypeTmpfs = "tmpfs"
|
||||
// TypeDevpts is the type for creating a devpts
|
||||
TypeDevpts = "devpts"
|
||||
// TypeTmpfs is the type for mounting tmpfs
|
||||
TypeTmpfs = "tmpfs"
|
||||
// TypeRamfs is the type for mounting ramfs
|
||||
TypeRamfs = "ramfs"
|
||||
// TypeVolume is the type for named volumes
|
||||
TypeVolume = "volume"
|
||||
)
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/containers/common/pkg/config"
|
||||
"github.com/containers/common/pkg/parse"
|
||||
"github.com/containers/podman/v4/libpod/define"
|
||||
"github.com/containers/podman/v4/pkg/rootless"
|
||||
"github.com/containers/podman/v4/pkg/specgen"
|
||||
"github.com/containers/podman/v4/pkg/util"
|
||||
spec "github.com/opencontainers/runtime-spec/specs-go"
|
||||
|
@ -211,8 +212,8 @@ func Mounts(mountFlag []string, configMounts []string) (map[string]spec.Mount, m
|
|||
}
|
||||
finalMounts[mount.Destination] = mount
|
||||
}
|
||||
case define.TypeTmpfs:
|
||||
mount, err := getTmpfsMount(tokens)
|
||||
case define.TypeTmpfs, define.TypeRamfs:
|
||||
mount, err := parseMemoryMount(tokens, mountType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -282,7 +283,7 @@ func Mounts(mountFlag []string, configMounts []string) (map[string]spec.Mount, m
|
|||
}
|
||||
|
||||
func parseMountOptions(mountType string, args []string) (*spec.Mount, error) {
|
||||
var setTmpcopyup, setRORW, setSuid, setDev, setExec, setRelabel, setOwnership bool
|
||||
var setTmpcopyup, setRORW, setSuid, setDev, setExec, setRelabel, setOwnership, setSwap bool
|
||||
|
||||
mnt := spec.Mount{}
|
||||
for _, val := range args {
|
||||
|
@ -359,6 +360,15 @@ func parseMountOptions(mountType string, args []string) (*spec.Mount, error) {
|
|||
}
|
||||
setSuid = true
|
||||
mnt.Options = append(mnt.Options, kv[0])
|
||||
case "noswap":
|
||||
if setSwap {
|
||||
return nil, fmt.Errorf("cannot pass 'noswap' mnt.Options more than once: %w", errOptionArg)
|
||||
}
|
||||
if rootless.IsRootless() {
|
||||
return nil, fmt.Errorf("the 'noswap' option is only allowed with rootful tmpfs mounts: %w", errOptionArg)
|
||||
}
|
||||
setSwap = true
|
||||
mnt.Options = append(mnt.Options, kv[0])
|
||||
case "relabel":
|
||||
if setRelabel {
|
||||
return nil, fmt.Errorf("cannot pass 'relabel' option more than once: %w", errOptionArg)
|
||||
|
@ -525,11 +535,11 @@ func getBindMount(args []string) (spec.Mount, error) {
|
|||
return newMount, nil
|
||||
}
|
||||
|
||||
// Parse a single tmpfs mount entry from the --mount flag
|
||||
func getTmpfsMount(args []string) (spec.Mount, error) {
|
||||
// Parse a single tmpfs/ramfs mount entry from the --mount flag
|
||||
func parseMemoryMount(args []string, mountType string) (spec.Mount, error) {
|
||||
newMount := spec.Mount{
|
||||
Type: define.TypeTmpfs,
|
||||
Source: define.TypeTmpfs,
|
||||
Type: mountType,
|
||||
Source: mountType,
|
||||
}
|
||||
|
||||
var err error
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/containers/podman/v4/libpod/define"
|
||||
"github.com/containers/podman/v4/pkg/rootless"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -27,7 +28,7 @@ type defaultMountOptions struct {
|
|||
// The sourcePath variable, if not empty, contains a bind mount source.
|
||||
func ProcessOptions(options []string, isTmpfs bool, sourcePath string) ([]string, error) {
|
||||
var (
|
||||
foundWrite, foundSize, foundProp, foundMode, foundExec, foundSuid, foundDev, foundCopyUp, foundBind, foundZ, foundU, foundOverlay, foundIdmap, foundCopy bool
|
||||
foundWrite, foundSize, foundProp, foundMode, foundExec, foundSuid, foundDev, foundCopyUp, foundBind, foundZ, foundU, foundOverlay, foundIdmap, foundCopy, foundNoSwap bool
|
||||
)
|
||||
|
||||
newOptions := make([]string, 0, len(options))
|
||||
|
@ -133,6 +134,20 @@ func ProcessOptions(options []string, isTmpfs bool, sourcePath string) ([]string
|
|||
foundCopyUp = true
|
||||
// do not propagate notmpcopyup to the OCI runtime
|
||||
continue
|
||||
case "noswap":
|
||||
|
||||
if !isTmpfs {
|
||||
return nil, fmt.Errorf("the 'noswap' option is only allowed with tmpfs mounts: %w", ErrBadMntOption)
|
||||
}
|
||||
if rootless.IsRootless() {
|
||||
return nil, fmt.Errorf("the 'noswap' option is only allowed with rootful tmpfs mounts: %w", ErrBadMntOption)
|
||||
}
|
||||
if foundNoSwap {
|
||||
return nil, fmt.Errorf("the 'tmpswap' option can only be set once: %w", ErrDupeMntOption)
|
||||
}
|
||||
foundNoSwap = true
|
||||
newOptions = append(newOptions, opt)
|
||||
continue
|
||||
case define.TypeBind, "rbind":
|
||||
if isTmpfs {
|
||||
return nil, fmt.Errorf("the 'bind' and 'rbind' options are not allowed with tmpfs mounts: %w", ErrBadMntOption)
|
||||
|
|
|
@ -294,4 +294,18 @@ EOF
|
|||
is "$output" "bar1.*bar2.*bar3" "Should match multiple source files on single destination directory"
|
||||
}
|
||||
|
||||
@test "podman mount noswap memory mounts" {
|
||||
# if volumes source and dest match then pass
|
||||
run_podman run --rm --mount type=ramfs,destination=${PODMAN_TMPDIR} $IMAGE stat -f -c "%T" ${PODMAN_TMPDIR}
|
||||
is "$output" "ramfs" "ramfs mounted"
|
||||
|
||||
if is_rootless; then
|
||||
run_podman 125 run --rm --mount type=tmpfs,destination=${PODMAN_TMPDIR},noswap $IMAGE stat -f -c "%T" ${PODMAN_TMPDIR}
|
||||
is "$output" "Error: the 'noswap' option is only allowed with rootful tmpfs mounts: must provide an argument for option" "noswap not supported in rootless mode"
|
||||
else
|
||||
run_podman run --rm --mount type=tmpfs,destination=${PODMAN_TMPDIR},noswap $IMAGE sh -c "mount| grep ${PODMAN_TMPDIR}"
|
||||
is "$output" ".*noswap" "tmpfs noswap mounted"
|
||||
fi
|
||||
}
|
||||
|
||||
# vim: filetype=sh
|
||||
|
|
Loading…
Reference in New Issue