vendor c/{buildah,common}: appendable containers.conf strings, Part 1

This change is the first step of integrating appendable string arrays
into containers.conf and starts with enabling the `Env`, `Mounts`, and
`Volumes` fields in the `[Containers]` table.

Both, Buildah and Podman, read (and sometimes write) the fields of the
`Config` struct at various places, so I decided to migrate the fields
step-by-step.  The ones in this change are most critical ones for
customers.  Once all string slices/arrays are migrated, the docs of
containers.conf will be updated.  The current changes are entirely
transparent to users.

Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
This commit is contained in:
Valentin Rothberg 2023-10-24 09:05:41 +02:00
parent 35121f67bf
commit 989afd910e
15 changed files with 251 additions and 78 deletions

4
go.mod
View File

@ -12,8 +12,8 @@ require (
github.com/container-orchestrated-devices/container-device-interface v0.6.1
github.com/containernetworking/cni v1.1.2
github.com/containernetworking/plugins v1.3.0
github.com/containers/buildah v1.32.1-0.20231016164031-ade05159a485
github.com/containers/common v0.56.1-0.20231023143107-8d0bd259cb7c
github.com/containers/buildah v1.32.1-0.20231024182922-ea815fea26a9
github.com/containers/common v0.56.1-0.20231024140609-79773286b53a
github.com/containers/conmon v2.0.20+incompatible
github.com/containers/gvisor-tap-vsock v0.7.1
github.com/containers/image/v5 v5.28.0

8
go.sum
View File

@ -249,10 +249,10 @@ github.com/containernetworking/plugins v0.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHV
github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRDjeJr6FLK6vuiUwoH7P8=
github.com/containernetworking/plugins v1.3.0 h1:QVNXMT6XloyMUoO2wUOqWTC1hWFV62Q6mVDp5H1HnjM=
github.com/containernetworking/plugins v1.3.0/go.mod h1:Pc2wcedTQQCVuROOOaLBPPxrEXqqXBFt3cZ+/yVg6l0=
github.com/containers/buildah v1.32.1-0.20231016164031-ade05159a485 h1:RqgxHW2iP5QJ3aRahT+KGI2aGXVZeZHTeulmeZQV0y0=
github.com/containers/buildah v1.32.1-0.20231016164031-ade05159a485/go.mod h1:gOMfotERP5Gz2pN+AnuM3ephId/YL9DmbOtVck6fWfE=
github.com/containers/common v0.56.1-0.20231023143107-8d0bd259cb7c h1:+5wIm8TWi18iu/WZhF6T9x693nmfpP9yqyrKCreDOOU=
github.com/containers/common v0.56.1-0.20231023143107-8d0bd259cb7c/go.mod h1:NGMoofxxOF8tno51JlwACw0HaUwaPS66h2N7CYJGFC0=
github.com/containers/buildah v1.32.1-0.20231024182922-ea815fea26a9 h1:z8+hkCt6vFxvP7pdz69WSABkIZBNvdGBBMRLwE7UXpQ=
github.com/containers/buildah v1.32.1-0.20231024182922-ea815fea26a9/go.mod h1:6SLsD6bQqppB1btFZSkdDHv2XxlXeqSyTUZsIroqssg=
github.com/containers/common v0.56.1-0.20231024140609-79773286b53a h1:1baXG5fPFMWklhYamf88+1vF2D5PbB2yenSKd9xLwm8=
github.com/containers/common v0.56.1-0.20231024140609-79773286b53a/go.mod h1:NGMoofxxOF8tno51JlwACw0HaUwaPS66h2N7CYJGFC0=
github.com/containers/conmon v2.0.20+incompatible h1:YbCVSFSCqFjjVwHTPINGdMX1F6JXHGTUje2ZYobNrkg=
github.com/containers/conmon v2.0.20+incompatible/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I=
github.com/containers/gvisor-tap-vsock v0.7.1 h1:+Rc+sOPplrkQb/BUXeN0ug8TxjgyrIqo/9P/eNS2A4c=

View File

@ -111,6 +111,37 @@ See 'podman create --help'" "--module must be specified before the command"
"--module=ENOENT"
}
@test "podman --module - append arrays" {
skip_if_remote "--module is not supported for remote clients"
random_data="expected_annotation_$(random_string 15)"
conf1_tmp="$PODMAN_TMPDIR/test1.conf"
conf2_tmp="$PODMAN_TMPDIR/test2.conf"
conf2_off_tmp="$PODMAN_TMPDIR/test2_off.conf"
cat > $conf1_tmp <<EOF
[containers]
env=["A=CONF1",{append=true}]
EOF
cat > $conf2_tmp <<EOF
[containers]
env=["B=CONF2"]
EOF
cat > $conf2_off_tmp <<EOF
[containers]
env=["B=CONF2_OFF",{append=false}]
EOF
# Once append is set, all subsequent loads (and the current) will be appended.
run_podman --module=$conf1_tmp --module=$conf2_tmp run --rm $IMAGE printenv A B
assert "$output" = "CONF1
CONF2"
# When explicitly turned off, values are replaced/overriden again.
run_podman 1 --module=$conf1_tmp --module=$conf2_off_tmp run --rm $IMAGE printenv A B
assert "$output" = "CONF2_OFF"
}
@test "podman --module - XDG_CONFIG_HOME" {
skip_if_remote "--module is not supported for remote clients"
skip_if_not_rootless "loading a module from XDG_CONFIG_HOME requires rootless"

View File

@ -32,7 +32,7 @@ env:
DEBIAN_NAME: "debian-13"
# Image identifiers
IMAGE_SUFFIX: "c20230816t191118z-f38f37d13"
IMAGE_SUFFIX: "c20231004t194547z-f39f38d13"
FEDORA_CACHE_IMAGE_NAME: "fedora-${IMAGE_SUFFIX}"
PRIOR_FEDORA_CACHE_IMAGE_NAME: "prior-fedora-${IMAGE_SUFFIX}"
DEBIAN_CACHE_IMAGE_NAME: "debian-${IMAGE_SUFFIX}"

View File

@ -229,6 +229,29 @@ func createPlatformContainer(options runUsingChrootExecSubprocOptions) error {
return errors.New("unsupported createPlatformContainer")
}
func mountFlagsForFSFlags(fsFlags uintptr) uintptr {
var mountFlags uintptr
for _, mapping := range []struct {
fsFlag uintptr
mountFlag uintptr
}{
{unix.ST_MANDLOCK, unix.MS_MANDLOCK},
{unix.ST_NOATIME, unix.MS_NOATIME},
{unix.ST_NODEV, unix.MS_NODEV},
{unix.ST_NODIRATIME, unix.MS_NODIRATIME},
{unix.ST_NOEXEC, unix.MS_NOEXEC},
{unix.ST_NOSUID, unix.MS_NOSUID},
{unix.ST_RDONLY, unix.MS_RDONLY},
{unix.ST_RELATIME, unix.MS_RELATIME},
{unix.ST_SYNCHRONOUS, unix.MS_SYNCHRONOUS},
} {
if fsFlags&mapping.fsFlag == mapping.fsFlag {
mountFlags |= mapping.mountFlag
}
}
return mountFlags
}
func makeReadOnly(mntpoint string, flags uintptr) error {
var fs unix.Statfs_t
// Make sure it's read-only.
@ -236,7 +259,9 @@ func makeReadOnly(mntpoint string, flags uintptr) error {
return fmt.Errorf("checking if directory %q was bound read-only: %w", mntpoint, err)
}
if fs.Flags&unix.ST_RDONLY == 0 {
if err := unix.Mount(mntpoint, mntpoint, "bind", flags|unix.MS_REMOUNT, ""); err != nil {
// All callers currently pass MS_RDONLY in "flags", but in case they stop doing
// that at some point in the future...
if err := unix.Mount(mntpoint, mntpoint, "bind", flags|unix.MS_RDONLY|unix.MS_REMOUNT|unix.MS_BIND, ""); err != nil {
return fmt.Errorf("remounting %s in mount namespace read-only: %w", mntpoint, err)
}
}
@ -268,7 +293,7 @@ func setupChrootBindMounts(spec *specs.Spec, bundlePath string) (undoBinds func(
// Now bind mount all of those things to be under the rootfs's location in this
// mount namespace.
commonFlags := uintptr(unix.MS_BIND | unix.MS_REC | unix.MS_PRIVATE)
bindFlags := commonFlags | unix.MS_NODEV
bindFlags := commonFlags
devFlags := commonFlags | unix.MS_NOEXEC | unix.MS_NOSUID | unix.MS_RDONLY
procFlags := devFlags | unix.MS_NODEV
sysFlags := devFlags | unix.MS_NODEV
@ -291,7 +316,7 @@ func setupChrootBindMounts(spec *specs.Spec, bundlePath string) (undoBinds func(
return undoBinds, fmt.Errorf("checking if directory %q was bound read-only: %w", subDev, err)
}
if fs.Flags&unix.ST_RDONLY == 0 {
if err := unix.Mount(subDev, subDev, "bind", devFlags|unix.MS_REMOUNT, ""); err != nil {
if err := unix.Mount(subDev, subDev, "bind", devFlags|unix.MS_REMOUNT|unix.MS_BIND, ""); err != nil {
return undoBinds, fmt.Errorf("remounting /dev in mount namespace read-only: %w", err)
}
}
@ -351,7 +376,7 @@ func setupChrootBindMounts(spec *specs.Spec, bundlePath string) (undoBinds func(
}
logrus.Debugf("bind mounted %q to %q", "/sys", filepath.Join(spec.Root.Path, "/sys"))
// Bind mount in everything we've been asked to mount.
// Bind, overlay, or tmpfs mount everything we've been asked to mount.
for _, m := range spec.Mounts {
// Skip anything that we just mounted.
switch m.Destination {
@ -369,12 +394,12 @@ func setupChrootBindMounts(spec *specs.Spec, bundlePath string) (undoBinds func(
continue
}
}
// Skip anything that isn't a bind or tmpfs mount.
// Skip anything that isn't a bind or overlay or tmpfs mount.
if m.Type != "bind" && m.Type != "tmpfs" && m.Type != "overlay" {
logrus.Debugf("skipping mount of type %q on %q", m.Type, m.Destination)
continue
}
// If the target is there, we can just mount it.
// If the target is already there, we can just mount over it.
var srcinfo os.FileInfo
switch m.Type {
case "bind":
@ -382,24 +407,22 @@ func setupChrootBindMounts(spec *specs.Spec, bundlePath string) (undoBinds func(
if err != nil {
return undoBinds, fmt.Errorf("examining %q for mounting in mount namespace: %w", m.Source, err)
}
case "overlay":
fallthrough
case "tmpfs":
case "overlay", "tmpfs":
srcinfo, err = os.Stat("/")
if err != nil {
return undoBinds, fmt.Errorf("examining / to use as a template for a %s: %w", m.Type, err)
return undoBinds, fmt.Errorf("examining / to use as a template for a %s mount: %w", m.Type, err)
}
}
target := filepath.Join(spec.Root.Path, m.Destination)
// Check if target is a symlink
// Check if target is a symlink.
stat, err := os.Lstat(target)
// If target is a symlink, follow the link and ensure the destination exists
// If target is a symlink, follow the link and ensure the destination exists.
if err == nil && stat != nil && (stat.Mode()&os.ModeSymlink != 0) {
target, err = copier.Eval(spec.Root.Path, m.Destination, copier.EvalOptions{})
if err != nil {
return nil, fmt.Errorf("evaluating symlink %q: %w", target, err)
}
// Stat the destination of the evaluated symlink
// Stat the destination of the evaluated symlink.
_, err = os.Stat(target)
}
if err != nil {
@ -407,7 +430,8 @@ func setupChrootBindMounts(spec *specs.Spec, bundlePath string) (undoBinds func(
if !errors.Is(err, os.ErrNotExist) {
return undoBinds, fmt.Errorf("examining %q for mounting in mount namespace: %w", target, err)
}
// The target isn't there yet, so create it.
// The target isn't there yet, so create it. If the source is a directory,
// we need a directory, otherwise we need a non-directory (i.e., a file).
if srcinfo.IsDir() {
if err = os.MkdirAll(target, 0755); err != nil {
return undoBinds, fmt.Errorf("creating mountpoint %q in mount namespace: %w", target, err)
@ -423,79 +447,94 @@ func setupChrootBindMounts(spec *specs.Spec, bundlePath string) (undoBinds func(
file.Close()
}
}
// Sort out which flags we're asking for, and what statfs() should be telling us
// if we successfully mounted with them.
requestFlags := uintptr(0)
expectedFlags := uintptr(0)
expectedImportantFlags := uintptr(0)
importantFlags := uintptr(0)
possibleImportantFlags := uintptr(unix.ST_NODEV | unix.ST_NOEXEC | unix.ST_NOSUID | unix.ST_RDONLY)
for _, option := range m.Options {
switch option {
case "nodev":
requestFlags |= unix.MS_NODEV
expectedFlags |= unix.ST_NODEV
importantFlags |= unix.ST_NODEV
expectedImportantFlags |= unix.ST_NODEV
case "dev":
requestFlags &= ^uintptr(unix.MS_NODEV)
expectedFlags &= ^uintptr(unix.ST_NODEV)
importantFlags |= unix.ST_NODEV
expectedImportantFlags &= ^uintptr(unix.ST_NODEV)
case "noexec":
requestFlags |= unix.MS_NOEXEC
expectedFlags |= unix.ST_NOEXEC
importantFlags |= unix.ST_NOEXEC
expectedImportantFlags |= unix.ST_NOEXEC
case "exec":
requestFlags &= ^uintptr(unix.MS_NOEXEC)
expectedFlags &= ^uintptr(unix.ST_NOEXEC)
importantFlags |= unix.ST_NOEXEC
expectedImportantFlags &= ^uintptr(unix.ST_NOEXEC)
case "nosuid":
requestFlags |= unix.MS_NOSUID
expectedFlags |= unix.ST_NOSUID
importantFlags |= unix.ST_NOSUID
expectedImportantFlags |= unix.ST_NOSUID
case "suid":
requestFlags &= ^uintptr(unix.MS_NOSUID)
expectedFlags &= ^uintptr(unix.ST_NOSUID)
importantFlags |= unix.ST_NOSUID
expectedImportantFlags &= ^uintptr(unix.ST_NOSUID)
case "ro":
requestFlags |= unix.MS_RDONLY
expectedFlags |= unix.ST_RDONLY
importantFlags |= unix.ST_RDONLY
expectedImportantFlags |= unix.ST_RDONLY
case "rw":
requestFlags &= ^uintptr(unix.MS_RDONLY)
expectedFlags &= ^uintptr(unix.ST_RDONLY)
importantFlags |= unix.ST_RDONLY
expectedImportantFlags &= ^uintptr(unix.ST_RDONLY)
}
}
switch m.Type {
case "bind":
// Do the bind mount.
logrus.Debugf("bind mounting %q on %q", m.Destination, filepath.Join(spec.Root.Path, m.Destination))
if err := unix.Mount(m.Source, target, "", bindFlags|requestFlags, ""); err != nil {
// Do the initial bind mount. We'll worry about the flags in a bit.
logrus.Debugf("bind mounting %q on %q %v", m.Destination, filepath.Join(spec.Root.Path, m.Destination), m.Options)
if err = unix.Mount(m.Source, target, "", bindFlags|requestFlags, ""); err != nil {
return undoBinds, fmt.Errorf("bind mounting %q from host to %q in mount namespace (%q): %w", m.Source, m.Destination, target, err)
}
if (requestFlags & unix.MS_RDONLY) != 0 {
if err = unix.Statfs(target, &fs); err != nil {
return undoBinds, fmt.Errorf("checking if directory %q was bound read-only: %w", target, err)
}
// we need to make sure these flags are maintained in the REMOUNT operation
additionalFlags := uintptr(fs.Flags) & (unix.MS_NOEXEC | unix.MS_NOSUID | unix.MS_NODEV)
if err := unix.Mount("", target, "", unix.MS_REMOUNT|unix.MS_BIND|unix.MS_RDONLY|additionalFlags, ""); err != nil {
return undoBinds, fmt.Errorf("setting flags on the bind mount %q from host to %q in mount namespace (%q): %w", m.Source, m.Destination, target, err)
}
}
logrus.Debugf("bind mounted %q to %q", m.Source, target)
case "tmpfs":
// Mount a tmpfs.
if err := mount.Mount(m.Source, target, m.Type, strings.Join(append(m.Options, "private"), ",")); err != nil {
return undoBinds, fmt.Errorf("mounting tmpfs to %q in mount namespace (%q, %q): %w", m.Destination, target, strings.Join(m.Options, ","), err)
// Mount a tmpfs. We'll worry about the flags in a bit.
if err = mount.Mount(m.Source, target, m.Type, strings.Join(append(m.Options, "private"), ",")); err != nil {
return undoBinds, fmt.Errorf("mounting tmpfs to %q in mount namespace (%q, %q): %w", m.Destination, target, strings.Join(append(m.Options, "private"), ","), err)
}
logrus.Debugf("mounted a tmpfs to %q", target)
case "overlay":
// Mount a overlay.
if err := mount.Mount(m.Source, target, m.Type, strings.Join(append(m.Options, "private"), ",")); err != nil {
return undoBinds, fmt.Errorf("mounting overlay to %q in mount namespace (%q, %q): %w", m.Destination, target, strings.Join(m.Options, ","), err)
// Mount an overlay. We'll worry about the flags in a bit.
if err = mount.Mount(m.Source, target, m.Type, strings.Join(append(m.Options, "private"), ",")); err != nil {
return undoBinds, fmt.Errorf("mounting overlay to %q in mount namespace (%q, %q): %w", m.Destination, target, strings.Join(append(m.Options, "private"), ","), err)
}
logrus.Debugf("mounted a overlay to %q", target)
}
// Time to worry about the flags.
if err = unix.Statfs(target, &fs); err != nil {
return undoBinds, fmt.Errorf("checking if directory %q was bound read-only: %w", target, err)
return undoBinds, fmt.Errorf("checking if volume %q was mounted with requested flags: %w", target, err)
}
if uintptr(fs.Flags)&expectedFlags != expectedFlags {
if err := unix.Mount(target, target, "bind", requestFlags|unix.MS_REMOUNT, ""); err != nil {
return undoBinds, fmt.Errorf("remounting %q in mount namespace with expected flags: %w", target, err)
effectiveImportantFlags := uintptr(fs.Flags) & importantFlags
if effectiveImportantFlags != expectedImportantFlags {
// Do a remount to try to get the desired flags to stick.
effectiveUnimportantFlags := uintptr(fs.Flags) & ^possibleImportantFlags
if err = unix.Mount(target, target, m.Type, unix.MS_REMOUNT|bindFlags|requestFlags|mountFlagsForFSFlags(effectiveUnimportantFlags), ""); err != nil {
return undoBinds, fmt.Errorf("remounting %q in mount namespace with flags %#x instead of %#x: %w", target, requestFlags, effectiveImportantFlags, err)
}
// Check if the desired flags stuck.
if err = unix.Statfs(target, &fs); err != nil {
return undoBinds, fmt.Errorf("checking if directory %q was remounted with requested flags %#x instead of %#x: %w", target, requestFlags, effectiveImportantFlags, err)
}
newEffectiveImportantFlags := uintptr(fs.Flags) & importantFlags
if newEffectiveImportantFlags != expectedImportantFlags {
return undoBinds, fmt.Errorf("unable to remount %q with requested flags %#x instead of %#x, just got %#x back", target, requestFlags, effectiveImportantFlags, newEffectiveImportantFlags)
}
}
}
// Set up any read-only paths that we need to. If we're running inside
// of a container, some of these locations will already be read-only.
// of a container, some of these locations will already be read-only, in
// which case can declare victory and move on.
for _, roPath := range spec.Linux.ReadonlyPaths {
r := filepath.Join(spec.Root.Path, roPath)
target, err := filepath.EvalSymlinks(r)
@ -515,12 +554,13 @@ func setupChrootBindMounts(spec *specs.Spec, bundlePath string) (undoBinds func(
}
return undoBinds, fmt.Errorf("checking if directory %q is already read-only: %w", target, err)
}
if fs.Flags&unix.ST_RDONLY != 0 {
if fs.Flags&unix.ST_RDONLY == unix.ST_RDONLY {
continue
}
// Mount the location over itself, so that we can remount it as read-only.
roFlags := uintptr(unix.MS_NODEV | unix.MS_NOEXEC | unix.MS_NOSUID | unix.MS_RDONLY)
if err := unix.Mount(target, target, "", roFlags|unix.MS_BIND|unix.MS_REC, ""); err != nil {
// Mount the location over itself, so that we can remount it as read-only, making
// sure to preserve any combination of nodev/noexec/nosuid that's already in play.
roFlags := mountFlagsForFSFlags(uintptr(fs.Flags)) | unix.MS_RDONLY
if err := unix.Mount(target, target, "", bindFlags|roFlags, ""); err != nil {
if errors.Is(err, os.ErrNotExist) {
// No target, no problem.
continue
@ -532,7 +572,7 @@ func setupChrootBindMounts(spec *specs.Spec, bundlePath string) (undoBinds func(
return undoBinds, fmt.Errorf("checking if directory %q was bound read-only: %w", target, err)
}
if fs.Flags&unix.ST_RDONLY == 0 {
if err := unix.Mount(target, target, "", roFlags|unix.MS_BIND|unix.MS_REMOUNT, ""); err != nil {
if err := unix.Mount(target, target, "", unix.MS_REMOUNT|unix.MS_RDONLY|bindFlags|mountFlagsForFSFlags(uintptr(fs.Flags)), ""); err != nil {
return undoBinds, fmt.Errorf("remounting %q in mount namespace read-only: %w", target, err)
}
}
@ -541,6 +581,7 @@ func setupChrootBindMounts(spec *specs.Spec, bundlePath string) (undoBinds func(
return undoBinds, fmt.Errorf("checking if directory %q was remounted read-only: %w", target, err)
}
if fs.Flags&unix.ST_RDONLY == 0 {
// Still not read only.
return undoBinds, fmt.Errorf("verifying that %q in mount namespace was remounted read-only: %w", target, err)
}
}
@ -578,7 +619,7 @@ func setupChrootBindMounts(spec *specs.Spec, bundlePath string) (undoBinds func(
if err = unix.Statfs(target, &statfs); err != nil {
return undoBinds, fmt.Errorf("checking if directory %q is a mountpoint: %w", target, err)
}
isReadOnly := statfs.Flags&unix.MS_RDONLY != 0
isReadOnly := statfs.Flags&unix.ST_RDONLY == unix.ST_RDONLY
// Check if any of the IDs we're mapping could read it.
var stat unix.Stat_t
if err = unix.Stat(target, &stat); err != nil {
@ -641,11 +682,11 @@ func setupChrootBindMounts(spec *specs.Spec, bundlePath string) (undoBinds func(
return undoBinds, fmt.Errorf("masking directory %q in mount namespace: %w", target, err)
}
if err = unix.Statfs(target, &fs); err != nil {
return undoBinds, fmt.Errorf("checking if directory %q was mounted read-only in mount namespace: %w", target, err)
return undoBinds, fmt.Errorf("checking if masked directory %q was mounted read-only in mount namespace: %w", target, err)
}
if fs.Flags&unix.ST_RDONLY == 0 {
if err = unix.Mount(target, target, "", roFlags|syscall.MS_REMOUNT, ""); err != nil {
return undoBinds, fmt.Errorf("making sure directory %q in mount namespace is read only: %w", target, err)
if err = unix.Mount(target, target, "", syscall.MS_REMOUNT|roFlags|mountFlagsForFSFlags(uintptr(fs.Flags)), ""); err != nil {
return undoBinds, fmt.Errorf("making sure masked directory %q in mount namespace is read only: %w", target, err)
}
}
}

View File

@ -13,6 +13,8 @@ import (
"github.com/sirupsen/logrus"
)
const seccompAvailable = true
// setSeccomp sets the seccomp filter for ourselves and any processes that we'll start.
func setSeccomp(spec *specs.Spec) error {
logrus.Debugf("setting seccomp configuration")

View File

@ -7,6 +7,8 @@ import (
"github.com/opencontainers/runtime-spec/specs-go"
)
const seccompAvailable = false
func setSeccomp(spec *specs.Spec) error {
// Ignore this on FreeBSD
return nil

View File

@ -9,6 +9,8 @@ import (
"github.com/opencontainers/runtime-spec/specs-go"
)
const seccompAvailable = false
func setSeccomp(spec *specs.Spec) error {
if spec.Linux.Seccomp != nil {
return errors.New("configured a seccomp filter without seccomp support?")

View File

@ -188,7 +188,7 @@ func newExecutor(logger *logrus.Logger, logPrefix string, store storage.Store, o
}
transientMounts := []Mount{}
for _, volume := range append(defaultContainerConfig.Containers.Volumes, options.TransientMounts...) {
for _, volume := range append(defaultContainerConfig.Volumes(), options.TransientMounts...) {
mount, err := parse.Volume(volume)
if err != nil {
return nil, err

View File

@ -374,7 +374,7 @@ func GetFromAndBudFlags(flags *FromAndBudResults, usernsResults *UserNSResults,
fs.StringArrayVar(&flags.SecurityOpt, "security-opt", []string{}, "security options (default [])")
fs.StringVar(&flags.ShmSize, "shm-size", defaultContainerConfig.Containers.ShmSize, "size of '/dev/shm'. The format is `<number><unit>`.")
fs.StringSliceVar(&flags.Ulimit, "ulimit", defaultContainerConfig.Containers.DefaultUlimits, "ulimit options")
fs.StringArrayVarP(&flags.Volumes, "volume", "v", defaultContainerConfig.Containers.Volumes, "bind mount a volume into the container")
fs.StringArrayVarP(&flags.Volumes, "volume", "v", defaultContainerConfig.Volumes(), "bind mount a volume into the container")
// Add in the usernamespace and namespaceflags
usernsFlags := GetUserNSFlags(usernsResults)

View File

@ -0,0 +1,92 @@
package attributedstring
import (
"bytes"
"fmt"
"github.com/BurntSushi/toml"
)
// Slice allows for extending a TOML string array with custom
// attributes that control how the array is marshaled into a Go string.
//
// Specifically, an Slice can be configured to avoid it being
// overridden by a subsequent unmarshal sequence. When the `append` attribute
// is specified, the array will be appended instead (e.g., `array=["9",
// {append=true}]`).
type Slice struct { // A "mixed-type array" in TOML.
// Note that the fields below _must_ be exported. Otherwise the TOML
// encoder would fail during type reflection.
Values []string
Attributes struct { // Using a struct allows for adding more attributes in the future.
Append *bool // Nil if not set by the user
}
}
// Get returns the Slice values or an empty string slice.
func (a *Slice) Get() []string {
if a.Values == nil {
return []string{}
}
return a.Values
}
// UnmarshalTOML is the custom unmarshal method for Slice.
func (a *Slice) UnmarshalTOML(data interface{}) error {
iFaceSlice, ok := data.([]interface{})
if !ok {
return fmt.Errorf("unable to cast to interface array: %v", data)
}
var loadedStrings []string
for _, x := range iFaceSlice { // Iterate over each item in the slice.
switch val := x.(type) {
case string: // Strings are directly appended to the slice.
loadedStrings = append(loadedStrings, val)
case map[string]interface{}: // The attribute struct is represented as a map.
for k, v := range val { // Iterate over all _supported_ keys.
switch k {
case "append":
boolVal, ok := v.(bool)
if !ok {
return fmt.Errorf("unable to cast append to bool: %v", k)
}
a.Attributes.Append = &boolVal
default: // Unsupported map key.
return fmt.Errorf("unsupported key %q in map: %v", k, val)
}
}
default: // Unsupported item.
return fmt.Errorf("unsupported item in attributed string slice: %v", x)
}
}
if a.Attributes.Append != nil && *a.Attributes.Append { // If _explicitly_ configured, append the loaded slice.
a.Values = append(a.Values, loadedStrings...)
} else { // Default: override the existing Slice.
a.Values = loadedStrings
}
return nil
}
// MarshalTOML is the custom marshal method for Slice.
func (a *Slice) MarshalTOML() ([]byte, error) {
iFaceSlice := make([]interface{}, 0, len(a.Values))
for _, x := range a.Values {
iFaceSlice = append(iFaceSlice, x)
}
if a.Attributes.Append != nil {
Attributes := make(map[string]any)
Attributes["append"] = *a.Attributes.Append
iFaceSlice = append(iFaceSlice, Attributes)
}
buf := new(bytes.Buffer)
enc := toml.NewEncoder(buf)
if err := enc.Encode(iFaceSlice); err != nil {
return nil, err
}
return buf.Bytes(), nil
}

View File

@ -10,6 +10,7 @@ import (
"strings"
"github.com/BurntSushi/toml"
"github.com/containers/common/internal/attributedstring"
"github.com/containers/common/libnetwork/types"
"github.com/containers/common/pkg/capabilities"
"github.com/containers/common/pkg/util"
@ -71,7 +72,7 @@ type ContainersConfig struct {
Devices []string `toml:"devices,omitempty"`
// Volumes to add to all containers
Volumes []string `toml:"volumes,omitempty"`
Volumes attributedstring.Slice `toml:"volumes,omitempty"`
// ApparmorProfile is the apparmor profile name which is used as the
// default for the runtime.
@ -133,7 +134,7 @@ type ContainersConfig struct {
EnableLabeledUsers bool `toml:"label_users,omitempty"`
// Env is the environment variable list for container process.
Env []string `toml:"env,omitempty"`
Env attributedstring.Slice `toml:"env,omitempty"`
// EnvHost Pass all host environment variables into the container.
EnvHost bool `toml:"env_host,omitempty"`
@ -171,7 +172,7 @@ type ContainersConfig struct {
LogTag string `toml:"log_tag,omitempty"`
// Mount to add to all containers
Mounts []string `toml:"mounts,omitempty"`
Mounts attributedstring.Slice `toml:"mounts,omitempty"`
// NetNS indicates how to create a network namespace for the container
NetNS string `toml:"netns,omitempty"`
@ -907,7 +908,7 @@ func (c *Config) GetDefaultEnvEx(envHost, httpProxy bool) []string {
}
}
}
return append(env, c.Containers.Env...)
return append(env, c.Containers.Env.Get()...)
}
// Capabilities returns the capabilities parses the Add and Drop capability

View File

@ -9,6 +9,7 @@ import (
"runtime"
"strings"
"github.com/containers/common/internal/attributedstring"
nettypes "github.com/containers/common/libnetwork/types"
"github.com/containers/common/pkg/apparmor"
"github.com/containers/common/pkg/cgroupv2"
@ -204,8 +205,8 @@ func defaultConfig() (*Config, error) {
Devices: []string{},
EnableKeyring: true,
EnableLabeling: selinuxEnabled(),
Env: []string{
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
Env: attributedstring.Slice{
Values: []string{"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"},
},
EnvHost: false,
HTTPProxy: true,
@ -214,7 +215,7 @@ func defaultConfig() (*Config, error) {
InitPath: "",
LogDriver: defaultLogDriver(),
LogSizeMax: DefaultLogSizeMax,
Mounts: []string{},
Mounts: attributedstring.Slice{},
NetNS: "private",
NoHosts: false,
PidNS: "private",
@ -224,7 +225,7 @@ func defaultConfig() (*Config, error) {
UTSNS: "private",
Umask: "0022",
UserNSSize: DefaultUserNSSize, // Deprecated
Volumes: []string{},
Volumes: attributedstring.Slice{},
},
Network: NetworkConfig{
DefaultNetwork: "podman",
@ -509,12 +510,12 @@ func (c *Config) Sysctls() []string {
// Volumes returns the default set of volumes that should be mounted in containers.
func (c *Config) Volumes() []string {
return c.Containers.Volumes
return c.Containers.Volumes.Get()
}
// Mounts returns the default set of mounts that should be mounted in containers.
func (c *Config) Mounts() []string {
return c.Containers.Mounts
return c.Containers.Mounts.Get()
}
// Devices returns the default additional devices for containers.
@ -539,7 +540,7 @@ func (c *Config) DNSOptions() []string {
// Env returns the default additional environment variables to add to containers.
func (c *Config) Env() []string {
return c.Containers.Env
return c.Containers.Env.Values
}
// IPCNS returns the default IPC Namespace configuration to run containers with.

5
vendor/modules.txt vendored
View File

@ -139,7 +139,7 @@ github.com/containernetworking/cni/pkg/version
# github.com/containernetworking/plugins v1.3.0
## explicit; go 1.20
github.com/containernetworking/plugins/pkg/ns
# github.com/containers/buildah v1.32.1-0.20231016164031-ade05159a485
# github.com/containers/buildah v1.32.1-0.20231024182922-ea815fea26a9
## explicit; go 1.18
github.com/containers/buildah
github.com/containers/buildah/bind
@ -167,8 +167,9 @@ github.com/containers/buildah/pkg/sshagent
github.com/containers/buildah/pkg/util
github.com/containers/buildah/pkg/volumes
github.com/containers/buildah/util
# github.com/containers/common v0.56.1-0.20231023143107-8d0bd259cb7c
# github.com/containers/common v0.56.1-0.20231024140609-79773286b53a
## explicit; go 1.18
github.com/containers/common/internal/attributedstring
github.com/containers/common/libimage
github.com/containers/common/libimage/define
github.com/containers/common/libimage/filter