mount: control namespace switching with --namespace-stage - #4029
mount: control namespace switching with --namespace-stage#4029cgoesche wants to merge 8 commits into
Conversation
|
TODOs:
|
martinetd
left a comment
There was a problem hiding this comment.
Thanks for tagging me in the issue (and for the implementation itself!)
Haven't had more than a very quick look, will do a more proper review when time allows
|
|
||
| *prepare*: During the mount preparation stage, i.e. fstab parsing, mount options preparation, source/target path canonicalization, file system type guessing and mount helper preparation. | ||
|
|
||
| *attach*: After the *prepare* stage and before the file system is mounted. |
There was a problem hiding this comment.
I've successfully used attach to bind something from outside a container to inside it
| *--namespace-stage* _stage_:: | ||
| Control at which stage of the mount process libmount should switch to the target namespace defined by *-N*. | ||
| + | ||
| Valid values for _stage_ are: |
There was a problem hiding this comment.
These keywords (prepare/attach) are backed by the implementation so they aren't wrong, but I'm not sure they're user-friendly: would source/dest be easier to understand? (as in, "enter namespace when evaluating the source device", "enter namespace when evaluating the mount destination")
I'm honestly not convinced myself as that fails to conveys nuances around fstab, and anyone who'd consider using this can take 5 minutes to understand, so I'm fine with the current words.
There was a problem hiding this comment.
Good points! Note that the current names are subject to change, as I first focused on the actual functionality rather than the semantics. Maybe we need to partition the stages even further, I'm thinking prep-source, prep-target, etc., but Im not sure.
| + | ||
| *all*: Switch to the target namespaces in all stages. | ||
|
|
||
| *prepare*: During the mount preparation stage, i.e. fstab parsing, mount options preparation, source/target path canonicalization, file system type guessing and mount helper preparation. |
There was a problem hiding this comment.
wow, I made systemd abort trying this one:
Feb 10 15:51:22 xx systemd[1]: Assertion 'path_is_absolute(p)' failed at src/basic/chase.c:648, function chase(). Aborting.
Feb 10 15:51:22 xx systemd[1]: Caught <ABRT>, from our own process.
I ran ./mount -N <pid> --namespace-stage=prepare --bind /foo . (only crashed the first time, /foo exists in the target namespace)
I'll need to take some time to play with that one. I would have expected the target namespace's /foo to be mounted locally but while it succeeded I didn't see anything change...
Using a directory other than ., mount fails with mount point does not exist, whether the path exists locally or in the dest namespace so I'm not sure how to use that one.
There was a problem hiding this comment.
(systemd bug made me curious, it was fixed in v259 here: systemd/systemd@e1f3d79 -- I'm still not quite sure what the mount command actually did though, I don't see anything in /proc/mounts that is odd...)
There was a problem hiding this comment.
Could you run the same command with LIBMOUNT_DEBUG=all please :D ?
There was a problem hiding this comment.
I think when using . as target, mount will just canonicalize that to /. I ran the same command and when I inspect the mounts with findmnt /, I can see the source tmpfs[/foo] for target /.
There was a problem hiding this comment.
Indeed, looks like it was transformed into / from the LIBMOUNT_DEBUG log -- unfortunately(?) that call fails on this machine so I'll try again from work during work hours tomorrow to understand what happened...
And I guess I've also earned the right to figure out what stage we'd need to mount a directory from a container into the host.. :)
Will try to have a look by next week
There was a problem hiding this comment.
Should probably split this thread, but...
- for namespace-stage attach, it's interesting, I can make this work with bwrap as you did but not with the same sequence using
podman run --pid=host(pid=host for simplicity finding the child pid but it's the same without) ... Looking at LIBMOUNT_DEBUG it now fails with EACCES but I'm sure I had ENOENT on open_tree() in the target namespace earlier, I don't get it... hmm it works if I run podman as root, something about user namespaces? but then I don't see why bwrap is any different here... Well, likely not a mount problem, so I guess it's fine? :|
$ podman run --pid=host -ti --rm docker.io/alpine
/ # echo $$
47642
# from other shell
# LIBMOUNT_DEBUG=all ./mount -N 47642 --namespace-stage=prepare --bind /tmp /mnt
...
47727: libmount: HOOK: [0x7fc4f3e6cc00]: BIND/MOVE
47727: libmount: CXT: [0x2ed48850]: alloc '__mount' data
47727: libmount: CXT: [0x2ed48850]: open_tree(path=/tmp clone)
47727: libmount: CXT: syscall 'open_tree' [failed: Permission denied]
47727: libmount: HOOK: [0x7fc4f3e6cc00]: init fs/tree failed [errno=13 Permission denied]
47727: libmount: HOOK: [0x7fc4f3e6cc00]: prepare mount done [rc=-13]
47727: libmount: CXT: [0x2ed48850]: <--- stage:prep [rc=-13 status=-13]
47727: libmount: CXT: [0x2ed48850]: mount: preparing failed
- for
., it resolves to/with--namespacebut I'd expect it to resolve to $PWD if the directory refers to a path within the current namespace (and, sure, why not / as it does right now or just error out for.inside the target namespace - using.here doesn't make sense to me)
This works with e.g.
mount --bind /tmp .
ls $PWD # accessing . is "beneath" the mount so need to use $PWD to check
# or
mount --bind . /mnt
ls /mnt
# or even
mount -N <pid> --namespace-stage attach --bind . /mnt
ls /mnt # inside target namespace
So it's surprising that mount -N <pid> --namespace-stage prepare --bind /whatever . doesn't behave similarly with . as $PWD-in-command-namespace.
There was a problem hiding this comment.
So, I tried something similar to the bwrap method with podman and I can successfully mount a filesystem from inside the container to the outside and vice versa.
1. Create the container
Shell 1
$ podman run --pid=host -ti --rm docker.io/alpine
/ # mkdir /tmp/container
/ # echo bar > /tmp/container/foo
/ # echo $$
316355
2. Mount file system from Container -> Host
Shell 2 on Host
$ mkdir /tmp/outside
$ sudo ./mount --namespace 316355 --namespace-stage prepare --bind /tmp/container /tmp/outside/
$ cat /tmp/outside/foo
bar
$ findmnt /tmp/outside
TARGET SOURCE FSTYPE OPTIONS
/tmp/outside
overlay[/tmp/container]
overla rw,relatime,context="system_u:object_r:container_file_t:....
3. Mount file system from Host -> Container
Shell 2 on Host
$ sudo umount /tmp/outside
$ echo b > /tmp/outside/a
$ sudo ./mount --namespace 316355 --namespace-stage attach --bind /tmp/outside /tmp/container
Shell 1 in Container
/ # cat /tmp/container/a
b
There was a problem hiding this comment.
Note, I ran podman only as a standard user.
There was a problem hiding this comment.
Thanks, this is the same as what I did so I'll check a bit more when time allows, but as said above I think it's not a mount problem so we can move forward about this one
There was a problem hiding this comment.
Sorry for the delay, for the first point (mount failing with EACCES) the problem seems to be that on my machine podman as user is using fuse.overlayfs, and accessing the fuse mount as a different user (even root) fails as such -- so that confirms there's no problem with mount on this... Well, I guess the error message could mention Permission denied without probing with LIBMOUNT_DEBUG, but that's orthogonal to this PR, so I think we're good here :)
(Anyway, I think podman now normally doesn't use fuse even as normal user, so that's probably something odd with my setup (perhaps leftovers from upgrading over an old setup that used it...))
That leaves the second point -- I still think . for destination in mount-stage=prepare context ought to be local to the mount process, or if that's difficult making it error out altogether. I don't see where making it behave like / with --namespace would make sense anywhere.
3b3cdea to
643e38d
Compare
The routines added in this patch help to control the switch to a given target namespace, that is, at what stages of the mount process a switch is appropriate. The desired stages are defined in a bitmask of MNT_NS_STAGE* flags, stored in the struct libmnt_ns_stage, which also contains the current stage (member: curr_stage). This current stage has to be set in specific places, as it is compared to the stage mask to determine if a switch is appropriate or not. Signed-off-by: Christian Goeschel Ndjomouo <cgoesc2@wgu.edu>
…ately Setting the current namespace stage in appropriate places ensures that the check before the switch is accurate. Signed-off-by: Christian Goeschel Ndjomouo <cgoesc2@wgu.edu>
Signed-off-by: Christian Goeschel Ndjomouo <cgoesc2@wgu.edu>
… flag This can allow higher level users to specify namespace stage flags with their respective names. As of now valid names are: - "all" = MNT_NS_STAGE_ALL - "prepare" = MNT_NS_STAGE_PREP - "attach" = MNT_NS_STAGE_ATTACH Signed-off-by: Christian Goeschel Ndjomouo <cgoesc2@wgu.edu>
This new option allows a user to control at which stage of the mount process libmount should switch to the target namespace defined with '--namespace'. The usefulness of this feature is stressed in situations where users wish to mount a filesystem from outside of the targeted namespace into it, e.g. to mount a filesystem into a container. Closes: util-linux#3884 Signed-off-by: Christian Goeschel Ndjomouo <cgoesc2@wgu.edu>
Signed-off-by: Christian Goeschel Ndjomouo <cgoesc2@wgu.edu>
Signed-off-by: Christian Goeschel Ndjomouo <cgoesc2@wgu.edu>
Signed-off-by: Christian Goeschel Ndjomouo <cgoesc2@wgu.edu>
643e38d to
119cfee
Compare
| * is appropriate for the current stage | ||
| */ | ||
| if (mnt_context_get_target_ns(cxt)) { | ||
| int rc = ns_switch_appropriate(cxt); |
There was a problem hiding this comment.
Hi, I'm looking for some way to manipulate container mounts and found this PR. Thanks for your work!
This changes the semantics of mnt_context_switch_ns() (think of mnt_context_switch_origin_ns()). Can we just let the callers call ns_switch_appropriate() instead?
|
Please note that I am currently focusing on stabilizing the new release. I will work on this PR later. |
This new option allows a user to control at which stage of
the mount process libmount should switch to the target
namespace defined with '--namespace'. The usefulness of this
feature is stressed in situations where users wish to mount a
filesystem from one namespace into the other, e.g. dynamically
mount a filesystem into a running container.