mirror of
https://github.com/containers/podman.git
synced 2025-10-26 10:45:26 +08:00
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>
This commit is contained in:
@ -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
|
||||||
|
|
||||||
|
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=volume,source=vol1,destination=/path/in/container,ro=true
|
||||||
|
|
||||||
type=tmpfs,tmpfs-size=512M,destination=/path/in/container
|
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).
|
· 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:
|
Options specific to image:
|
||||||
|
|
||||||
· rw, readwrite: true or false (default).
|
· rw, readwrite: true or false (default).
|
||||||
@ -627,6 +631,8 @@ Current supported mount TYPEs are **bind**, **volume**, **image**, **tmpfs** and
|
|||||||
|
|
||||||
. relabel: shared, private.
|
. 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:
|
Options specific to tmpfs:
|
||||||
|
|
||||||
· ro, readonly: true or false (default).
|
· 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.
|
· 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*
|
#### **--name**=*name*
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|
||||||
|
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=volume,source=vol1,destination=/path/in/container,ro=true
|
||||||
|
|
||||||
type=tmpfs,tmpfs-size=512M,destination=/path/in/container
|
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).
|
· 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:
|
Options specific to image:
|
||||||
|
|
||||||
· rw, readwrite: true or false (default).
|
· rw, readwrite: true or false (default).
|
||||||
@ -647,6 +651,8 @@ Current supported mount TYPEs are **bind**, **volume**, **image**, **tmpfs** and
|
|||||||
|
|
||||||
. relabel: shared, private.
|
. 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:
|
Options specific to tmpfs:
|
||||||
|
|
||||||
· ro, readonly: true or false (default).
|
· 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.
|
· 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*
|
#### **--name**=*name*
|
||||||
|
|
||||||
Assign a name to the container.
|
Assign a name to the container.
|
||||||
|
|||||||
@ -243,7 +243,7 @@ func getBindMount(args []string) (spec.Mount, error) {
|
|||||||
Type: define.TypeBind,
|
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 {
|
for _, val := range args {
|
||||||
kv := strings.SplitN(val, "=", 2)
|
kv := strings.SplitN(val, "=", 2)
|
||||||
@ -343,6 +343,18 @@ func getBindMount(args []string) (spec.Mount, error) {
|
|||||||
default:
|
default:
|
||||||
return newMount, errors.Wrapf(util.ErrBadMntOption, "%s mount option must be 'private' or 'shared'", kv[0])
|
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":
|
case "consistency":
|
||||||
// Often used on MACs and mistakenly on Linux platforms.
|
// Often used on MACs and mistakenly on Linux platforms.
|
||||||
// Since Docker ignores this option so shall we.
|
// Since Docker ignores this option so shall we.
|
||||||
@ -375,7 +387,7 @@ func getTmpfsMount(args []string) (spec.Mount, error) {
|
|||||||
Source: define.TypeTmpfs,
|
Source: define.TypeTmpfs,
|
||||||
}
|
}
|
||||||
|
|
||||||
var setDest, setRORW, setSuid, setDev, setExec, setTmpcopyup bool
|
var setDest, setRORW, setSuid, setDev, setExec, setTmpcopyup, setOwnership bool
|
||||||
|
|
||||||
for _, val := range args {
|
for _, val := range args {
|
||||||
kv := strings.SplitN(val, "=", 2)
|
kv := strings.SplitN(val, "=", 2)
|
||||||
@ -431,6 +443,18 @@ func getTmpfsMount(args []string) (spec.Mount, error) {
|
|||||||
}
|
}
|
||||||
newMount.Destination = filepath.Clean(kv[1])
|
newMount.Destination = filepath.Clean(kv[1])
|
||||||
setDest = true
|
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":
|
case "consistency":
|
||||||
// Often used on MACs and mistakenly on Linux platforms.
|
// Often used on MACs and mistakenly on Linux platforms.
|
||||||
// Since Docker ignores this option so shall we.
|
// 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) {
|
func getNamedVolume(args []string) (*specgen.NamedVolume, error) {
|
||||||
newVolume := new(specgen.NamedVolume)
|
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 {
|
for _, val := range args {
|
||||||
kv := strings.SplitN(val, "=", 2)
|
kv := strings.SplitN(val, "=", 2)
|
||||||
@ -532,6 +556,18 @@ func getNamedVolume(args []string) (*specgen.NamedVolume, error) {
|
|||||||
}
|
}
|
||||||
newVolume.Dest = filepath.Clean(kv[1])
|
newVolume.Dest = filepath.Clean(kv[1])
|
||||||
setDest = true
|
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":
|
case "consistency":
|
||||||
// Often used on MACs and mistakenly on Linux platforms.
|
// Often used on MACs and mistakenly on Linux platforms.
|
||||||
// Since Docker ignores this option so shall we.
|
// Since Docker ignores this option so shall we.
|
||||||
@ -628,3 +664,24 @@ func getTmpfsMounts(tmpfsFlag []string) (map[string]spec.Mount, error) {
|
|||||||
}
|
}
|
||||||
return m, nil
|
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
|
||||||
|
}
|
||||||
|
|||||||
@ -614,7 +614,7 @@ VOLUME /test/`, ALPINE)
|
|||||||
Expect(len(session.OutputToStringArray())).To(Equal(2))
|
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")
|
SkipIfRemote("Overlay volumes only work locally")
|
||||||
|
|
||||||
u, err := user.Current()
|
u, err := user.Current()
|
||||||
@ -665,6 +665,65 @@ VOLUME /test/`, ALPINE)
|
|||||||
Expect(found).Should(BeTrue())
|
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() {
|
It("volume permissions after run", func() {
|
||||||
imgName := "testimg"
|
imgName := "testimg"
|
||||||
dockerfile := fmt.Sprintf(`FROM %s
|
dockerfile := fmt.Sprintf(`FROM %s
|
||||||
|
|||||||
Reference in New Issue
Block a user