diff mbox series

[RFC,1/2] utils/docker-run: make it compatible with SELinux

Message ID 20230726212009.221147-1-thomas.petazzoni@bootlin.com
State Superseded
Headers show
Series [RFC,1/2] utils/docker-run: make it compatible with SELinux | expand

Commit Message

Thomas Petazzoni July 26, 2023, 9:20 p.m. UTC
After switching to a fresh Fedora 38 installation with SELinux
disabled, we noticed that utils/docker-run doesn't work as the
applications running inside the container are not allowed to accept
the data mounted through the bind mount.

Turns out that Docker has a "Z" option to do the appropriate magic for
SELinux. However, this "Z" option is only available for --volume, not
for --mount, as explained in
https://docs.docker.com/storage/bind-mounts/.

So, this commit partially reverts 7f2020f9040f ("utils/docker-run:
improve user experience") that switched from --volume to
--mount. However, the justification in 7f2020f9040f to switch from
--volume to --mount was "Docker will create the destination if it does
not exist", but the current Docker documentation seems to say exactly
the opposite:

    If you use -v or --volume to bind-mount a file or directory that
    does not yet exist on the Docker host, -v creates the endpoint for
    you. It is always created as a directory.

    If you use --mount to bind-mount a file or directory that does not
    yet exist on the Docker host, Docker does not automatically create
    it for you, but generates an error.

Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
---
NOTE: I am not a Docker expert, and I certainly don't know if this is
the right solution, and I would appreciate feedback from folks with
more Docker experience.
---
 utils/docker-run | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

Comments

Antoine Tenart July 27, 2023, 8:13 a.m. UTC | #1
Hi Thomas,

Quoting Thomas Petazzoni via buildroot (2023-07-26 23:20:07)
> 
> diff --git a/utils/docker-run b/utils/docker-run
> index 17c587a484..eee1aad7a4 100755
> --- a/utils/docker-run
> +++ b/utils/docker-run
> @@ -12,8 +12,8 @@ declare -a docker_opts=(
>      -i
>      --rm
>      --user "$(id -u):$(id -g)"
> -    --mount "type=bind,src=${MAIN_DIR},dst=${MAIN_DIR}"
> -    --mount "type=bind,src=${GIT_DIR},dst=${GIT_DIR}"
> +    --volume "${MAIN_DIR}:${MAIN_DIR}:Z"
> +    --volume "${GIT_DIR}:${GIT_DIR}:Z"

Using Z will label all the files in MAIN_DIR and GIT_DIR with a private
label and IIRC that means relabeling all files each time a new container
is started; which can take quite some time if there are lots if files in
there. However z can be used to label files with a shared label and they
won't be relabeled after the first run.

Antoine
Thomas Petazzoni July 27, 2023, 8:48 a.m. UTC | #2
Hello Antoine,

On Thu, 27 Jul 2023 10:13:53 +0200
Antoine Tenart <atenart@kernel.org> wrote:

> > +    --volume "${MAIN_DIR}:${MAIN_DIR}:Z"
> > +    --volume "${GIT_DIR}:${GIT_DIR}:Z"  
> 
> Using Z will label all the files in MAIN_DIR and GIT_DIR with a private
> label and IIRC that means relabeling all files each time a new container
> is started; which can take quite some time if there are lots if files in
> there. However z can be used to label files with a shared label and they
> won't be relabeled after the first run.

Thanks for the hint, makes sense!

Do you know why --mount, which is apparently "superior" and recommended
over --volume, does not support this SELinux labeling mechanism?

Thomas
Antoine Tenart July 27, 2023, 10:19 a.m. UTC | #3
Quoting Thomas Petazzoni via buildroot (2023-07-27 10:48:21)
> 
> Do you know why --mount, which is apparently "superior" and recommended
> over --volume, does not support this SELinux labeling mechanism?

IIRC --mount was introduced for better consistency in the docker args
and supporting names parameters (I don't know the full reasoning but
it's probably linked to supporting APIs -like yaml definitions- in a
better way?). While doing so they tried to fix issues in --volume and
labeling files on mount was one of them.

The issue is both logical and practical: files should be labeled when
being created (not when being mounted) and relabeling files "magically"
can cause issues (eg. don't `-v /home:/home:z` !). The reasoning with
the new --mount option is a directory should be created and configured
on the host and then only mounted by containers (hence the directory is
not anymore created if not present when using --mount).

So here, idk, not a docker expert :) I'd say using relabeling of a
directory that is under Buildroot's control is probably OK. While
replying, had a quick look at this and it seems the preferred solution
would be instead to use the `--security-opt label=disable` option:
labels would be kept in sync with the host and I guess the goal of using
Docker here is not for isolating the build but to have a known
environment. I never played with that option so please investigate
before switching to it.

Antoine
Thomas Petazzoni July 27, 2023, 10:24 a.m. UTC | #4
Hello,

On Thu, 27 Jul 2023 12:19:48 +0200
Antoine Tenart <atenart@kernel.org> wrote:

> IIRC --mount was introduced for better consistency in the docker args
> and supporting names parameters (I don't know the full reasoning but
> it's probably linked to supporting APIs -like yaml definitions- in a
> better way?). While doing so they tried to fix issues in --volume and
> labeling files on mount was one of them.
> 
> The issue is both logical and practical: files should be labeled when
> being created (not when being mounted) and relabeling files "magically"
> can cause issues (eg. don't `-v /home:/home:z` !). The reasoning with
> the new --mount option is a directory should be created and configured
> on the host and then only mounted by containers (hence the directory is
> not anymore created if not present when using --mount).
> 
> So here, idk, not a docker expert :) I'd say using relabeling of a
> directory that is under Buildroot's control is probably OK. While
> replying, had a quick look at this and it seems the preferred solution
> would be instead to use the `--security-opt label=disable` option:
> labels would be kept in sync with the host and I guess the goal of using
> Docker here is not for isolating the build but to have a known
> environment. I never played with that option so please investigate
> before switching to it.

Thanks for the hint!

I tried --security-opt label=disable, and it works, at least it fixes
my permission issue, without having to do the z/Z thing. Probably it's
a better option than using z/Z that has the side effect of adding
SELinux labels on all files being mounted? To be honest, I'm not clear
on the consequences of --security-opt label=disable.

Thomas
Antoine Tenart July 27, 2023, 10:50 a.m. UTC | #5
Quoting Thomas Petazzoni (2023-07-27 12:24:49)
> On Thu, 27 Jul 2023 12:19:48 +0200
> Antoine Tenart <atenart@kernel.org> wrote:
> 
> > IIRC --mount was introduced for better consistency in the docker args
> > and supporting names parameters (I don't know the full reasoning but
> > it's probably linked to supporting APIs -like yaml definitions- in a
> > better way?). While doing so they tried to fix issues in --volume and
> > labeling files on mount was one of them.
> > 
> > The issue is both logical and practical: files should be labeled when
> > being created (not when being mounted) and relabeling files "magically"
> > can cause issues (eg. don't `-v /home:/home:z` !). The reasoning with
> > the new --mount option is a directory should be created and configured
> > on the host and then only mounted by containers (hence the directory is
> > not anymore created if not present when using --mount).
> > 
> > So here, idk, not a docker expert :) I'd say using relabeling of a
> > directory that is under Buildroot's control is probably OK. While
> > replying, had a quick look at this and it seems the preferred solution
> > would be instead to use the `--security-opt label=disable` option:
> > labels would be kept in sync with the host and I guess the goal of using
> > Docker here is not for isolating the build but to have a known
> > environment. I never played with that option so please investigate
> > before switching to it.
> 
> I tried --security-opt label=disable, and it works, at least it fixes
> my permission issue, without having to do the z/Z thing. Probably it's
> a better option than using z/Z that has the side effect of adding
> SELinux labels on all files being mounted? To be honest, I'm not clear
> on the consequences of --security-opt label=disable.

I agree on not relabeling files if that's an option. Not sure about the
consequences of `--security-opt label=disable` too. My understanding is
the containerized process won't be labeled with a container-specific
label, but they still could be labeled with a permissive one not 100%
matching the user label.

Without having a full understanding of the implications we can still
expect child processes not to have more rights than the parent and
created file labels to match what would have happened on the host (could
you check?). Given the goal is not to isolate the build, my guess is
that is fine and the best option.

Antoine
Antoine Tenart July 27, 2023, 11:47 a.m. UTC | #6
Quoting Antoine Tenart (2023-07-27 12:50:17)
> 
> Without having a full understanding of the implications we can still
> expect child processes not to have more rights than the parent

Hmm, my point doesn't work for non-rootless containers, the Docker
deamon runs as root. Not that this changes the other points.
Yann E. MORIN July 27, 2023, 4:21 p.m. UTC | #7
Thomas, All,

On 2023-07-26 23:20 +0200, Thomas Petazzoni spake thusly:
> After switching to a fresh Fedora 38 installation with SELinux
> disabled, we noticed that utils/docker-run doesn't work as the
> applications running inside the container are not allowed to accept
> the data mounted through the bind mount.
> 
> Turns out that Docker has a "Z" option to do the appropriate magic for
> SELinux. However, this "Z" option is only available for --volume, not
> for --mount, as explained in
> https://docs.docker.com/storage/bind-mounts/.

hat about those systems that do not have SELinux: how is z/Z going to
work for those?

> So, this commit partially reverts 7f2020f9040f ("utils/docker-run:
> improve user experience") that switched from --volume to
> --mount. However, the justification in 7f2020f9040f to switch from
> --volume to --mount was "Docker will create the destination if it does
> not exist", but the current Docker documentation seems to say exactly
> the opposite:
> 
>     If you use -v or --volume to bind-mount a file or directory that
>     does not yet exist on the Docker host, -v creates the endpoint for
>     you. It is always created as a directory.
> 
>     If you use --mount to bind-mount a file or directory that does not
>     yet exist on the Docker host, Docker does not automatically create
>     it for you, but generates an error.

Note that this is about "on the docker host", so the source, while the
commit mentions "the destination in the container".

I can't where I initially read that, since the pointer in 7f2020f9040f
is no longer valid (the anchor no longer exists).

> Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
> ---
> NOTE: I am not a Docker expert, and I certainly don't know if this is
> the right solution, and I would appreciate feedback from folks with
> more Docker experience.
> ---
>  utils/docker-run | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/utils/docker-run b/utils/docker-run
> index 17c587a484..eee1aad7a4 100755
> --- a/utils/docker-run
> +++ b/utils/docker-run
> @@ -12,8 +12,8 @@ declare -a docker_opts=(
>      -i
>      --rm
>      --user "$(id -u):$(id -g)"
> -    --mount "type=bind,src=${MAIN_DIR},dst=${MAIN_DIR}"
> -    --mount "type=bind,src=${GIT_DIR},dst=${GIT_DIR}"
> +    --volume "${MAIN_DIR}:${MAIN_DIR}:Z"
> +    --volume "${GIT_DIR}:${GIT_DIR}:Z"
>      --workdir "${MAIN_DIR}"
>  )
>  if tty -s; then
> -- 
> 2.41.0
>
Thomas Petazzoni July 28, 2023, 7:24 a.m. UTC | #8
Hello Christian,

On Thu, 27 Jul 2023 20:13:08 -0700
Christian Stewart <christian@paral.in> wrote:

> On Wed, Jul 26, 2023 at 2:20 PM Thomas Petazzoni
> <thomas.petazzoni@bootlin.com> wrote:
> > -    --mount "type=bind,src=${MAIN_DIR},dst=${MAIN_DIR}"
> > -    --mount "type=bind,src=${GIT_DIR},dst=${GIT_DIR}"
> > +    --volume "${MAIN_DIR}:${MAIN_DIR}:Z"
> > +    --volume "${GIT_DIR}:${GIT_DIR}:Z"
> >      --workdir "${MAIN_DIR}"  
> 
> What is the purpose of the GIT_DIR mount here, doesn't MAIN_DIR contain .git?

This is explained in 791c163b2f9f07d4c02b18eabd9b195918e1c603:

commit 791c163b2f9f07d4c02b18eabd9b195918e1c603
Author: Yann E. MORIN <yann.morin.1998@free.fr>
Date:   Sat May 6 23:46:18 2023 +0200

    utils/docker-run: make it work in workdirs/woktrees
    
    It is quite customary to use a single repository with multiple workdirs,
    one for each active branch, with either the aging 'git new-workdir' or
    the more recent 'git worktree'.
    
    However, in a workdir/worktree, most entries in .git/ are only symlinks
    to the actual files in the main repository.
    
    Currently, utils/docker-run only bind-mounts the current working copy.
    If that is a workdir/worktree, then it is going to be missing the actual
    git data, resulting in errors like:
    
        $ ./utils/docker-run make check-package
        fatal: not a git repository (or any parent up to mount point [....]/buildroot)
        Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
        No files to check style
        make: *** [Makefile:1257: check-package] Error 1
    
    So, we also bind-mount the actual git directory. If that is a subdir
    of the current working copy, then it is already mounted and thus the
    bind-mount is superfluous but harmless; for simplicity, we mount it
    unconditionally.
    
    Signed-off-by: Yann E. MORIN <yann.morin.1998@free.fr>
    Cc: Ricardo Martincoski <ricardo.martincoski@datacom.com.br>

> I think this mount might be causing some unpredictable behavior in the
> host-go package, but it's just a hunch at the moment.

Did you confirm this?

Thomas
Yann E. MORIN Aug. 9, 2023, 9:32 p.m. UTC | #9
Thomas, All,

On 2023-07-26 23:20 +0200, Thomas Petazzoni via buildroot spake thusly:
> After switching to a fresh Fedora 38 installation with SELinux
> disabled, we noticed that utils/docker-run doesn't work as the
> applications running inside the container are not allowed to accept
> the data mounted through the bind mount.
> 
> Turns out that Docker has a "Z" option to do the appropriate magic for
> SELinux. However, this "Z" option is only available for --volume, not
> for --mount, as explained in
> https://docs.docker.com/storage/bind-mounts/.
> 
> So, this commit partially reverts 7f2020f9040f ("utils/docker-run:
> improve user experience") that switched from --volume to
> --mount. However, the justification in 7f2020f9040f to switch from
> --volume to --mount was "Docker will create the destination if it does
> not exist", but the current Docker documentation seems to say exactly
> the opposite:
> 
>     If you use -v or --volume to bind-mount a file or directory that
>     does not yet exist on the Docker host, -v creates the endpoint for
>     you. It is always created as a directory.
> 
>     If you use --mount to bind-mount a file or directory that does not
>     yet exist on the Docker host, Docker does not automatically create
>     it for you, but generates an error.
> 
> Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>

I've included this change in my respin:

    https://patchwork.ozlabs.org/project/buildroot/list/?series=368134

In doing so, I switched to the proposal by Antoine, to use
--security-opt label=disable

I did not see an adverse effect on a non-SELinux host, and you reported
that it also worked for you, so let's ship it.

I've also included patch 2 in the respin.

Regards,
Yann E. MORIN.

> ---
> NOTE: I am not a Docker expert, and I certainly don't know if this is
> the right solution, and I would appreciate feedback from folks with
> more Docker experience.
> ---
>  utils/docker-run | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/utils/docker-run b/utils/docker-run
> index 17c587a484..eee1aad7a4 100755
> --- a/utils/docker-run
> +++ b/utils/docker-run
> @@ -12,8 +12,8 @@ declare -a docker_opts=(
>      -i
>      --rm
>      --user "$(id -u):$(id -g)"
> -    --mount "type=bind,src=${MAIN_DIR},dst=${MAIN_DIR}"
> -    --mount "type=bind,src=${GIT_DIR},dst=${GIT_DIR}"
> +    --volume "${MAIN_DIR}:${MAIN_DIR}:Z"
> +    --volume "${GIT_DIR}:${GIT_DIR}:Z"
>      --workdir "${MAIN_DIR}"
>  )
>  if tty -s; then
> -- 
> 2.41.0
> 
> _______________________________________________
> buildroot mailing list
> buildroot@buildroot.org
> https://lists.buildroot.org/mailman/listinfo/buildroot
diff mbox series

Patch

diff --git a/utils/docker-run b/utils/docker-run
index 17c587a484..eee1aad7a4 100755
--- a/utils/docker-run
+++ b/utils/docker-run
@@ -12,8 +12,8 @@  declare -a docker_opts=(
     -i
     --rm
     --user "$(id -u):$(id -g)"
-    --mount "type=bind,src=${MAIN_DIR},dst=${MAIN_DIR}"
-    --mount "type=bind,src=${GIT_DIR},dst=${GIT_DIR}"
+    --volume "${MAIN_DIR}:${MAIN_DIR}:Z"
+    --volume "${GIT_DIR}:${GIT_DIR}:Z"
     --workdir "${MAIN_DIR}"
 )
 if tty -s; then