summaryrefslogtreecommitdiff
path: root/pkg/spec
diff options
context:
space:
mode:
authorOpenShift Merge Robot <openshift-merge-robot@users.noreply.github.com>2019-09-04 22:43:41 +0200
committerGitHub <noreply@github.com>2019-09-04 22:43:41 +0200
commitab44484becdb2a5806c6fa892e65846f05f98d0d (patch)
tree4626cf53b9089163f76bebdadec93b2df8cfb54a /pkg/spec
parent52f24540988f4ce1da7949325716121d9a78decc (diff)
parent5f1550157842c4e93f728efbc12720d87790c26f (diff)
downloadpodman-ab44484becdb2a5806c6fa892e65846f05f98d0d.tar.gz
podman-ab44484becdb2a5806c6fa892e65846f05f98d0d.tar.bz2
podman-ab44484becdb2a5806c6fa892e65846f05f98d0d.zip
Merge pull request #3876 from mheon/fix_mount_flags
Allow suid, exec, dev mount options to cancel nosuid/noexec/nodev
Diffstat (limited to 'pkg/spec')
-rw-r--r--pkg/spec/spec.go64
-rw-r--r--pkg/spec/storage.go190
2 files changed, 151 insertions, 103 deletions
diff --git a/pkg/spec/spec.go b/pkg/spec/spec.go
index 156d6849d..44bbda885 100644
--- a/pkg/spec/spec.go
+++ b/pkg/spec/spec.go
@@ -2,13 +2,11 @@ package createconfig
import (
"os"
- "path/filepath"
"strings"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/pkg/cgroups"
"github.com/containers/libpod/pkg/rootless"
- pmount "github.com/containers/storage/pkg/mount"
"github.com/docker/docker/oci/caps"
"github.com/docker/go-units"
"github.com/opencontainers/runc/libcontainer/user"
@@ -368,7 +366,11 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
// BIND MOUNTS
configSpec.Mounts = supercedeUserMounts(userMounts, configSpec.Mounts)
// Process mounts to ensure correct options
- configSpec.Mounts = initFSMounts(configSpec.Mounts)
+ finalMounts, err := initFSMounts(configSpec.Mounts)
+ if err != nil {
+ return nil, err
+ }
+ configSpec.Mounts = finalMounts
// BLOCK IO
blkio, err := config.CreateBlockIO()
@@ -394,43 +396,6 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
}
}
- // Make sure that the bind mounts keep options like nosuid, noexec, nodev.
- mounts, err := pmount.GetMounts()
- if err != nil {
- return nil, err
- }
- for i := range configSpec.Mounts {
- m := &configSpec.Mounts[i]
- isBind := false
- for _, o := range m.Options {
- if o == "bind" || o == "rbind" {
- isBind = true
- break
- }
- }
- if !isBind {
- continue
- }
- mount, err := findMount(m.Source, mounts)
- if err != nil {
- return nil, err
- }
- if mount == nil {
- continue
- }
- next_option:
- for _, o := range strings.Split(mount.Opts, ",") {
- if o == "nosuid" || o == "noexec" || o == "nodev" {
- for _, e := range m.Options {
- if e == o {
- continue next_option
- }
- }
- m.Options = append(m.Options, o)
- }
- }
- }
-
// Add annotations
if configSpec.Annotations == nil {
configSpec.Annotations = make(map[string]string)
@@ -490,25 +455,6 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
return configSpec, nil
}
-func findMount(target string, mounts []*pmount.Info) (*pmount.Info, error) {
- var err error
- target, err = filepath.Abs(target)
- if err != nil {
- return nil, errors.Wrapf(err, "cannot resolve %s", target)
- }
- var bestSoFar *pmount.Info
- for _, i := range mounts {
- if bestSoFar != nil && len(bestSoFar.Mountpoint) > len(i.Mountpoint) {
- // Won't be better than what we have already found
- continue
- }
- if strings.HasPrefix(target, i.Mountpoint) {
- bestSoFar = i
- }
- }
- return bestSoFar, nil
-}
-
func blockAccessToKernelFilesystems(config *CreateConfig, g *generate.Generator) {
if !config.Privileged {
for _, mp := range []string{
diff --git a/pkg/spec/storage.go b/pkg/spec/storage.go
index b634f4cac..bc0eaad6d 100644
--- a/pkg/spec/storage.go
+++ b/pkg/spec/storage.go
@@ -10,6 +10,7 @@ import (
"github.com/containers/buildah/pkg/parse"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/pkg/util"
+ pmount "github.com/containers/storage/pkg/mount"
"github.com/containers/storage/pkg/stringid"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
@@ -160,22 +161,18 @@ func (config *CreateConfig) parseVolumes(runtime *libpod.Runtime) ([]spec.Mount,
}
// If requested, add tmpfs filesystems for read-only containers.
- // Need to keep track of which we created, so we don't modify options
- // for them later...
- readonlyTmpfs := map[string]bool{
- "/tmp": false,
- "/var/tmp": false,
- "/run": false,
- }
if config.ReadOnlyRootfs && config.ReadOnlyTmpfs {
+ readonlyTmpfs := []string{"/tmp", "/var/tmp", "/run"}
options := []string{"rw", "rprivate", "nosuid", "nodev", "tmpcopyup"}
- for dest := range readonlyTmpfs {
+ for _, dest := range readonlyTmpfs {
if _, ok := baseMounts[dest]; ok {
continue
}
localOpts := options
if dest == "/run" {
localOpts = append(localOpts, "noexec", "size=65536k")
+ } else {
+ localOpts = append(localOpts, "exec")
}
baseMounts[dest] = spec.Mount{
Destination: dest,
@@ -183,7 +180,6 @@ func (config *CreateConfig) parseVolumes(runtime *libpod.Runtime) ([]spec.Mount,
Source: "tmpfs",
Options: localOpts,
}
- readonlyTmpfs[dest] = true
}
}
@@ -202,16 +198,6 @@ func (config *CreateConfig) parseVolumes(runtime *libpod.Runtime) ([]spec.Mount,
// Final step: maps to arrays
finalMounts := make([]spec.Mount, 0, len(baseMounts))
for _, mount := range baseMounts {
- // All user-added tmpfs mounts need their options processed.
- // Exception: mounts added by the ReadOnlyTmpfs option, which
- // contain several exceptions to normal options rules.
- if mount.Type == TypeTmpfs && !readonlyTmpfs[mount.Destination] {
- opts, err := util.ProcessTmpfsOptions(mount.Options)
- if err != nil {
- return nil, nil, err
- }
- mount.Options = opts
- }
if mount.Type == TypeBind {
absSrc, err := filepath.Abs(mount.Source)
if err != nil {
@@ -226,9 +212,6 @@ func (config *CreateConfig) parseVolumes(runtime *libpod.Runtime) ([]spec.Mount,
finalVolumes = append(finalVolumes, volume)
}
- logrus.Debugf("Got mounts: %v", finalMounts)
- logrus.Debugf("Got volumes: %v", finalVolumes)
-
return finalMounts, finalVolumes, nil
}
@@ -250,14 +233,17 @@ func (config *CreateConfig) getVolumesFrom(runtime *libpod.Runtime) (map[string]
splitVol = strings.SplitN(vol, ":", 2)
)
if len(splitVol) == 2 {
- if strings.Contains(splitVol[1], "Z") ||
- strings.Contains(splitVol[1], "private") ||
- strings.Contains(splitVol[1], "slave") ||
- strings.Contains(splitVol[1], "shared") {
- return nil, nil, errors.Errorf("invalid options %q, can only specify 'ro', 'rw', and 'z", splitVol[1])
+ splitOpts := strings.Split(splitVol[1], ",")
+ for _, checkOpt := range splitOpts {
+ switch checkOpt {
+ case "z", "ro", "rw":
+ // Do nothing, these are valid options
+ default:
+ return nil, nil, errors.Errorf("invalid options %q, can only specify 'ro', 'rw', and 'z'", splitVol[1])
+ }
}
- if options, err = parse.ValidateVolumeOpts(strings.Split(splitVol[1], ",")); err != nil {
+ if options, err = parse.ValidateVolumeOpts(splitOpts); err != nil {
return nil, nil, err
}
}
@@ -403,9 +389,7 @@ func getBindMount(args []string) (spec.Mount, error) {
Type: TypeBind,
}
- setSource := false
- setDest := false
- setRORW := false
+ var setSource, setDest, setRORW, setSuid, setDev, setExec bool
for _, val := range args {
kv := strings.Split(val, "=")
@@ -440,9 +424,23 @@ func getBindMount(args []string) (spec.Mount, error) {
} else {
return newMount, errors.Wrapf(optionArgError, "badly formatted option %q", val)
}
- case "nosuid", "nodev", "noexec":
- // TODO: detect duplication of these options.
- // (Is this necessary?)
+ case "nosuid", "suid":
+ if setSuid {
+ return newMount, errors.Wrapf(optionArgError, "cannot pass 'nosuid' and 'suid' options more than once")
+ }
+ setSuid = true
+ newMount.Options = append(newMount.Options, kv[0])
+ case "nodev", "dev":
+ if setDev {
+ return newMount, errors.Wrapf(optionArgError, "cannot pass 'nodev' and 'dev' options more than once")
+ }
+ setDev = true
+ newMount.Options = append(newMount.Options, kv[0])
+ case "noexec", "exec":
+ if setExec {
+ return newMount, errors.Wrapf(optionArgError, "cannot pass 'noexec' and 'exec' options more than once")
+ }
+ setExec = true
newMount.Options = append(newMount.Options, kv[0])
case "shared", "rshared", "private", "rprivate", "slave", "rslave", "Z", "z":
newMount.Options = append(newMount.Options, kv[0])
@@ -497,12 +495,34 @@ func getTmpfsMount(args []string) (spec.Mount, error) {
Source: TypeTmpfs,
}
- setDest := false
+ var setDest, setRORW, setSuid, setDev, setExec bool
for _, val := range args {
kv := strings.Split(val, "=")
switch kv[0] {
- case "ro", "nosuid", "nodev", "noexec":
+ case "ro", "rw":
+ if setRORW {
+ return newMount, errors.Wrapf(optionArgError, "cannot pass 'ro' and 'rw' options more than once")
+ }
+ setRORW = true
+ newMount.Options = append(newMount.Options, kv[0])
+ case "nosuid", "suid":
+ if setSuid {
+ return newMount, errors.Wrapf(optionArgError, "cannot pass 'nosuid' and 'suid' options more than once")
+ }
+ setSuid = true
+ newMount.Options = append(newMount.Options, kv[0])
+ case "nodev", "dev":
+ if setDev {
+ return newMount, errors.Wrapf(optionArgError, "cannot pass 'nodev' and 'dev' options more than once")
+ }
+ setDev = true
+ newMount.Options = append(newMount.Options, kv[0])
+ case "noexec", "exec":
+ if setExec {
+ return newMount, errors.Wrapf(optionArgError, "cannot pass 'noexec' and 'exec' options more than once")
+ }
+ setExec = true
newMount.Options = append(newMount.Options, kv[0])
case "tmpfs-mode":
if len(kv) == 1 {
@@ -543,14 +563,34 @@ func getTmpfsMount(args []string) (spec.Mount, error) {
func getNamedVolume(args []string) (*libpod.ContainerNamedVolume, error) {
newVolume := new(libpod.ContainerNamedVolume)
- setSource := false
- setDest := false
+ var setSource, setDest, setRORW, setSuid, setDev, setExec bool
for _, val := range args {
kv := strings.Split(val, "=")
switch kv[0] {
- case "ro", "nosuid", "nodev", "noexec":
- // TODO: detect duplication of these options
+ case "ro", "rw":
+ if setRORW {
+ return nil, errors.Wrapf(optionArgError, "cannot pass 'ro' and 'rw' options more than once")
+ }
+ setRORW = true
+ newVolume.Options = append(newVolume.Options, kv[0])
+ case "nosuid", "suid":
+ if setSuid {
+ return nil, errors.Wrapf(optionArgError, "cannot pass 'nosuid' and 'suid' options more than once")
+ }
+ setSuid = true
+ newVolume.Options = append(newVolume.Options, kv[0])
+ case "nodev", "dev":
+ if setDev {
+ return nil, errors.Wrapf(optionArgError, "cannot pass 'nodev' and 'dev' options more than once")
+ }
+ setDev = true
+ newVolume.Options = append(newVolume.Options, kv[0])
+ case "noexec", "exec":
+ if setExec {
+ return nil, errors.Wrapf(optionArgError, "cannot pass 'noexec' and 'exec' options more than once")
+ }
+ setExec = true
newVolume.Options = append(newVolume.Options, kv[0])
case "volume-label":
return nil, errors.Errorf("the --volume-label option is not presently implemented")
@@ -692,6 +732,9 @@ func (config *CreateConfig) getTmpfsMounts() (map[string]spec.Mount, error) {
var options []string
spliti := strings.Split(i, ":")
destPath := spliti[0]
+ if err := parse.ValidateVolumeCtrDir(spliti[0]); err != nil {
+ return nil, err
+ }
if len(spliti) > 1 {
options = strings.Split(spliti[1], ",")
}
@@ -775,16 +818,75 @@ func supercedeUserMounts(mounts []spec.Mount, configMount []spec.Mount) []spec.M
}
// Ensure mount options on all mounts are correct
-func initFSMounts(inputMounts []spec.Mount) []spec.Mount {
+func initFSMounts(inputMounts []spec.Mount) ([]spec.Mount, error) {
+ // We need to look up mounts so we can figure out the proper mount flags
+ // to apply.
+ systemMounts, err := pmount.GetMounts()
+ if err != nil {
+ return nil, errors.Wrapf(err, "error retrieving system mounts to look up mount options")
+ }
+
+ // TODO: We probably don't need to re-build the mounts array
var mounts []spec.Mount
for _, m := range inputMounts {
if m.Type == TypeBind {
- m.Options = util.ProcessOptions(m.Options)
+ baseMnt, err := findMount(m.Destination, systemMounts)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error looking up mountpoint for mount %s", m.Destination)
+ }
+ var noexec, nosuid, nodev bool
+ for _, baseOpt := range strings.Split(baseMnt.Opts, ",") {
+ switch baseOpt {
+ case "noexec":
+ noexec = true
+ case "nosuid":
+ nosuid = true
+ case "nodev":
+ nodev = true
+ }
+ }
+
+ defaultMountOpts := new(util.DefaultMountOptions)
+ defaultMountOpts.Noexec = noexec
+ defaultMountOpts.Nosuid = nosuid
+ defaultMountOpts.Nodev = nodev
+
+ opts, err := util.ProcessOptions(m.Options, false, defaultMountOpts)
+ if err != nil {
+ return nil, err
+ }
+ m.Options = opts
}
if m.Type == TypeTmpfs && filepath.Clean(m.Destination) != "/dev" {
- m.Options = append(m.Options, "tmpcopyup")
+ opts, err := util.ProcessOptions(m.Options, true, nil)
+ if err != nil {
+ return nil, err
+ }
+ m.Options = opts
}
+
mounts = append(mounts, m)
}
- return mounts
+ return mounts, nil
+}
+
+// TODO: We could make this a bit faster by building a tree of the mountpoints
+// and traversing it to identify the correct mount.
+func findMount(target string, mounts []*pmount.Info) (*pmount.Info, error) {
+ var err error
+ target, err = filepath.Abs(target)
+ if err != nil {
+ return nil, errors.Wrapf(err, "cannot resolve %s", target)
+ }
+ var bestSoFar *pmount.Info
+ for _, i := range mounts {
+ if bestSoFar != nil && len(bestSoFar.Mountpoint) > len(i.Mountpoint) {
+ // Won't be better than what we have already found
+ continue
+ }
+ if strings.HasPrefix(target, i.Mountpoint) {
+ bestSoFar = i
+ }
+ }
+ return bestSoFar, nil
}