aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEduardo Vega <edvegavalerio@gmail.com>2021-09-19 10:17:08 -0600
committerEduardo Vega <edvegavalerio@gmail.com>2021-09-22 15:42:16 -0600
commited3c4a89d61a89673d08825aeee21430957d5185 (patch)
tree5d0e143790ef3457fd4a206ba89bd3379a019cd5
parent08e1bb54c3cb390b1f2821222961805bb689df99 (diff)
downloadpodman-ed3c4a89d61a89673d08825aeee21430957d5185.tar.gz
podman-ed3c4a89d61a89673d08825aeee21430957d5185.tar.bz2
podman-ed3c4a89d61a89673d08825aeee21430957d5185.zip
Add support for :U flag with --mount option
The :U flag can be used to change the ownership of source volumes based on the UID, GID of the container. This is only supported by the --volume option, this will allow to use --mount option as well. Signed-off-by: Eduardo Vega <edvegavalerio@gmail.com>
-rw-r--r--docs/source/markdown/podman-create.1.md7
-rw-r--r--docs/source/markdown/podman-run.1.md8
-rw-r--r--pkg/specgenutil/volumes.go63
-rw-r--r--test/e2e/run_volume_test.go61
4 files changed, 135 insertions, 4 deletions
diff --git a/docs/source/markdown/podman-create.1.md b/docs/source/markdown/podman-create.1.md
index 0c48f105e..4a20e4e45 100644
--- a/docs/source/markdown/podman-create.1.md
+++ b/docs/source/markdown/podman-create.1.md
@@ -595,6 +595,8 @@ Current supported mount TYPEs are **bind**, **volume**, **image**, **tmpfs** and
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=volume,source=vol1,destination=/path/in/container,ro=true
type=tmpfs,tmpfs-size=512M,destination=/path/in/container
@@ -613,6 +615,8 @@ Current supported mount TYPEs are **bind**, **volume**, **image**, **tmpfs** and
· ro, readonly: true or false (default).
+ . 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 image:
· rw, readwrite: true or false (default).
@@ -627,6 +631,8 @@ Current supported mount TYPEs are **bind**, **volume**, **image**, **tmpfs** and
. relabel: shared, private.
+ . 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:
· ro, readonly: true or false (default).
@@ -639,6 +645,7 @@ Current supported mount TYPEs are **bind**, **volume**, **image**, **tmpfs** and
· notmpcopyup: Disable copying files from the image to the tmpfs.
+ . 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.
#### **--name**=*name*
diff --git a/docs/source/markdown/podman-run.1.md b/docs/source/markdown/podman-run.1.md
index 7b6a56fc6..7a2e09dab 100644
--- a/docs/source/markdown/podman-run.1.md
+++ b/docs/source/markdown/podman-run.1.md
@@ -615,6 +615,8 @@ Current supported mount TYPEs are **bind**, **volume**, **image**, **tmpfs** and
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=volume,source=vol1,destination=/path/in/container,ro=true
type=tmpfs,tmpfs-size=512M,destination=/path/in/container
@@ -633,6 +635,8 @@ Current supported mount TYPEs are **bind**, **volume**, **image**, **tmpfs** and
· ro, readonly: true or false (default).
+ . 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 image:
· rw, readwrite: true or false (default).
@@ -647,6 +651,8 @@ Current supported mount TYPEs are **bind**, **volume**, **image**, **tmpfs** and
. relabel: shared, private.
+ . 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:
· ro, readonly: true or false (default).
@@ -659,6 +665,8 @@ Current supported mount TYPEs are **bind**, **volume**, **image**, **tmpfs** and
· notmpcopyup: Disable copying files from the image to the tmpfs.
+ . 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.
+
#### **--name**=*name*
Assign a name to the container.
diff --git a/pkg/specgenutil/volumes.go b/pkg/specgenutil/volumes.go
index 0ed08198f..3ce96164f 100644
--- a/pkg/specgenutil/volumes.go
+++ b/pkg/specgenutil/volumes.go
@@ -243,7 +243,7 @@ func getBindMount(args []string) (spec.Mount, error) {
Type: define.TypeBind,
}
- var setSource, setDest, setRORW, setSuid, setDev, setExec, setRelabel bool
+ var setSource, setDest, setRORW, setSuid, setDev, setExec, setRelabel, setOwnership bool
for _, val := range args {
kv := strings.SplitN(val, "=", 2)
@@ -343,6 +343,18 @@ func getBindMount(args []string) (spec.Mount, error) {
default:
return newMount, errors.Wrapf(util.ErrBadMntOption, "%s mount option must be 'private' or 'shared'", kv[0])
}
+ case "U", "chown":
+ if setOwnership {
+ return newMount, errors.Wrapf(optionArgError, "cannot pass 'U' or 'chown' option more than once")
+ }
+ ok, err := validChownFlag(val)
+ if err != nil {
+ return newMount, err
+ }
+ if ok {
+ newMount.Options = append(newMount.Options, "U")
+ }
+ setOwnership = true
case "consistency":
// Often used on MACs and mistakenly on Linux platforms.
// Since Docker ignores this option so shall we.
@@ -375,7 +387,7 @@ func getTmpfsMount(args []string) (spec.Mount, error) {
Source: define.TypeTmpfs,
}
- var setDest, setRORW, setSuid, setDev, setExec, setTmpcopyup bool
+ var setDest, setRORW, setSuid, setDev, setExec, setTmpcopyup, setOwnership bool
for _, val := range args {
kv := strings.SplitN(val, "=", 2)
@@ -431,6 +443,18 @@ func getTmpfsMount(args []string) (spec.Mount, error) {
}
newMount.Destination = filepath.Clean(kv[1])
setDest = true
+ case "U", "chown":
+ if setOwnership {
+ return newMount, errors.Wrapf(optionArgError, "cannot pass 'U' or 'chown' option more than once")
+ }
+ ok, err := validChownFlag(val)
+ if err != nil {
+ return newMount, err
+ }
+ if ok {
+ newMount.Options = append(newMount.Options, "U")
+ }
+ setOwnership = true
case "consistency":
// Often used on MACs and mistakenly on Linux platforms.
// Since Docker ignores this option so shall we.
@@ -486,7 +510,7 @@ func getDevptsMount(args []string) (spec.Mount, error) {
func getNamedVolume(args []string) (*specgen.NamedVolume, error) {
newVolume := new(specgen.NamedVolume)
- var setSource, setDest, setRORW, setSuid, setDev, setExec bool
+ var setSource, setDest, setRORW, setSuid, setDev, setExec, setOwnership bool
for _, val := range args {
kv := strings.SplitN(val, "=", 2)
@@ -532,6 +556,18 @@ func getNamedVolume(args []string) (*specgen.NamedVolume, error) {
}
newVolume.Dest = filepath.Clean(kv[1])
setDest = true
+ case "U", "chown":
+ if setOwnership {
+ return newVolume, errors.Wrapf(optionArgError, "cannot pass 'U' or 'chown' option more than once")
+ }
+ ok, err := validChownFlag(val)
+ if err != nil {
+ return newVolume, err
+ }
+ if ok {
+ newVolume.Options = append(newVolume.Options, "U")
+ }
+ setOwnership = true
case "consistency":
// Often used on MACs and mistakenly on Linux platforms.
// Since Docker ignores this option so shall we.
@@ -628,3 +664,24 @@ func getTmpfsMounts(tmpfsFlag []string) (map[string]spec.Mount, error) {
}
return m, nil
}
+
+// validChownFlag ensures that the U or chown flag is correctly used
+func validChownFlag(flag string) (bool, error) {
+ kv := strings.SplitN(flag, "=", 2)
+ switch len(kv) {
+ case 1:
+ case 2:
+ // U=[true|false]
+ switch strings.ToLower(kv[1]) {
+ case "true":
+ case "false":
+ return false, nil
+ default:
+ return false, errors.Wrapf(optionArgError, "'U' or 'chown' must be set to true or false, instead received %q", kv[1])
+ }
+ default:
+ return false, errors.Wrapf(optionArgError, "badly formatted option %q", flag)
+ }
+
+ return true, nil
+}
diff --git a/test/e2e/run_volume_test.go b/test/e2e/run_volume_test.go
index 59937b6c0..690c53de6 100644
--- a/test/e2e/run_volume_test.go
+++ b/test/e2e/run_volume_test.go
@@ -614,7 +614,7 @@ VOLUME /test/`, ALPINE)
Expect(len(session.OutputToStringArray())).To(Equal(2))
})
- It("podman run with U volume flag", func() {
+ It("podman run with --volume and U flag", func() {
SkipIfRemote("Overlay volumes only work locally")
u, err := user.Current()
@@ -665,6 +665,65 @@ VOLUME /test/`, ALPINE)
Expect(found).Should(BeTrue())
})
+ It("podman run with --mount and U flag", func() {
+ u, err := user.Current()
+ Expect(err).To(BeNil())
+ name := u.Username
+ if name == "root" {
+ name = "containers"
+ }
+
+ content, err := ioutil.ReadFile("/etc/subuid")
+ if err != nil {
+ Skip("cannot read /etc/subuid")
+ }
+
+ if !strings.Contains(string(content), name) {
+ Skip("cannot find mappings for the current user")
+ }
+
+ mountPath := filepath.Join(podmanTest.TempDir, "foo")
+ os.Mkdir(mountPath, 0755)
+
+ // false bind mount
+ vol := "type=bind,src=" + mountPath + ",dst=" + dest + ",U=false"
+ session := podmanTest.Podman([]string{"run", "--rm", "--user", "888:888", "--mount", vol, ALPINE, "stat", "-c", "%u:%g", dest})
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+ Expect(session.OutputToString()).ShouldNot(Equal("888:888"))
+
+ // invalid bind mount
+ vol = "type=bind,src=" + mountPath + ",dst=" + dest + ",U=invalid"
+ session = podmanTest.Podman([]string{"run", "--rm", "--user", "888:888", "--mount", vol, ALPINE, "stat", "-c", "%u:%g", dest})
+ session.WaitWithDefaultTimeout()
+ Expect(session).To(ExitWithError())
+
+ // true bind mount
+ vol = "type=bind,src=" + mountPath + ",dst=" + dest + ",U=true"
+ session = podmanTest.Podman([]string{"run", "--rm", "--user", "888:888", "--mount", vol, ALPINE, "stat", "-c", "%u:%g", dest})
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+ Expect(session.OutputToString()).Should(Equal("888:888"))
+
+ // tmpfs mount
+ vol = "type=tmpfs," + "dst=" + dest + ",chown"
+ session = podmanTest.Podman([]string{"run", "--rm", "--user", "888:888", "--mount", vol, ALPINE, "stat", "-c", "%u:%g", dest})
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+ Expect(session.OutputToString()).Should(Equal("888:888"))
+
+ // named volume mount
+ namedVolume := podmanTest.Podman([]string{"volume", "create", "foo"})
+ namedVolume.WaitWithDefaultTimeout()
+ Expect(namedVolume).Should(Exit(0))
+
+ vol = "type=volume,src=foo,dst=" + dest + ",chown=true"
+ session = podmanTest.Podman([]string{"run", "--rm", "--user", "888:888", "--mount", vol, ALPINE, "stat", "-c", "%u:%g", dest})
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+ Expect(session.OutputToString()).Should(Equal("888:888"))
+ })
+
It("volume permissions after run", func() {
imgName := "testimg"
dockerfile := fmt.Sprintf(`FROM %s