From 9f86685cc76991e983bf126a37e3f7f319247ba8 Mon Sep 17 00:00:00 2001 From: Johan Thomsen Date: Thu, 30 Jul 2020 16:20:50 +0200 Subject: [PATCH 1/2] dockerTools: fix permissions on /nix/store --- pkgs/build-support/docker/stream_layered_image.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pkgs/build-support/docker/stream_layered_image.py b/pkgs/build-support/docker/stream_layered_image.py index 8ffd336fce4..ffb6ba0ade4 100644 --- a/pkgs/build-support/docker/stream_layered_image.py +++ b/pkgs/build-support/docker/stream_layered_image.py @@ -74,6 +74,10 @@ def archive_paths_to(obj, paths, mtime, add_nix, filter=None): ti.gname = "root" return filter(ti) + def nix_root(ti): + ti.mode = 0o0555 # r-xr-xr-x + return ti + def dir(path): ti = tarfile.TarInfo(path) ti.type = tarfile.DIRTYPE @@ -84,8 +88,8 @@ def archive_paths_to(obj, paths, mtime, add_nix, filter=None): # these directories first when building layer tarballs. But # we don't need them on the customisation layer. if add_nix: - tar.addfile(apply_filters(dir("/nix"))) - tar.addfile(apply_filters(dir("/nix/store"))) + tar.addfile(apply_filters(nix_root(dir("/nix")))) + tar.addfile(apply_filters(nix_root(dir("/nix/store")))) for path in paths: path = pathlib.Path(path) From f5db415e2f75f09048f98b96cee1a6e0d48c3a5d Mon Sep 17 00:00:00 2001 From: Johan Thomsen Date: Thu, 30 Jul 2020 17:18:41 +0200 Subject: [PATCH 2/2] nixos/tests/dockerTools: add test for running non-root containers with buildLayeredImage Co-authored-by: Robert Hensing --- nixos/tests/docker-tools.nix | 10 +++++++ pkgs/build-support/docker/examples.nix | 36 ++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/nixos/tests/docker-tools.nix b/nixos/tests/docker-tools.nix index 5ce67695de4..2543801ae8b 100644 --- a/nixos/tests/docker-tools.nix +++ b/nixos/tests/docker-tools.nix @@ -79,6 +79,16 @@ import ./make-test-python.nix ({ pkgs, ... }: { "docker rmi ${examples.nix.imageName}", ) + with subtest( + "Ensure (layered) nix store has correct permissions " + "and that the container starts when its process does not have uid 0" + ): + docker.succeed( + "docker load --input='${examples.bashLayeredWithUser}'", + "docker run -u somebody --rm ${examples.bashLayeredWithUser.imageName} ${pkgs.bash}/bin/bash -c 'test 555 == $(stat --format=%a /nix) && test 555 == $(stat --format=%a /nix/store)'", + "docker rmi ${examples.bashLayeredWithUser.imageName}", + ) + with subtest("The nix binary symlinks are intact"): docker.succeed( "docker load --input='${examples.nix}'", diff --git a/pkgs/build-support/docker/examples.nix b/pkgs/build-support/docker/examples.nix index 0d907c2d64b..bc107471762 100644 --- a/pkgs/build-support/docker/examples.nix +++ b/pkgs/build-support/docker/examples.nix @@ -382,4 +382,40 @@ rec { contents = pkgs.bashInteractive; }; + # buildLayeredImage with non-root user + bashLayeredWithUser = + let + nonRootShadowSetup = { user, uid, gid ? uid }: with pkgs; [ + ( + writeTextDir "etc/shadow" '' + root:!x::::::: + ${user}:!::::::: + '' + ) + ( + writeTextDir "etc/passwd" '' + root:x:0:0::/root:${runtimeShell} + ${user}:x:${toString uid}:${toString gid}::/home/${user}: + '' + ) + ( + writeTextDir "etc/group" '' + root:x:0: + ${user}:x:${toString gid}: + '' + ) + ( + writeTextDir "etc/gshadow" '' + root:x:: + ${user}:x:: + '' + ) + ]; + in + pkgs.dockerTools.buildLayeredImage { + name = "bash-layered-with-user"; + tag = "latest"; + contents = [ pkgs.bash pkgs.coreutils (nonRootShadowSetup { uid = 999; user = "somebody"; }) ]; + }; + }