Skip to content

Commit

Permalink
Merge pull request #9854 from the-sun-will-rise-tomorrow/docker-user
Browse files Browse the repository at this point in the history
docker: Allow building for non-root user
  • Loading branch information
roberth authored Nov 18, 2024
2 parents 4387c5a + c9433c0 commit d8d5929
Show file tree
Hide file tree
Showing 6 changed files with 163 additions and 17 deletions.
10 changes: 8 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ jobs:
- run: exec bash -c "nix-channel --update && nix-env -iA nixpkgs.hello && hello"

docker_push_image:
needs: [check_secrets, tests]
needs: [check_secrets, tests, vm_tests]
permissions:
contents: read
packages: write
Expand Down Expand Up @@ -194,7 +194,13 @@ jobs:
- uses: actions/checkout@v4
- uses: DeterminateSystems/nix-installer-action@main
- uses: DeterminateSystems/magic-nix-cache-action@main
- run: nix build -L .#hydraJobs.tests.githubFlakes .#hydraJobs.tests.tarballFlakes .#hydraJobs.tests.functional_user
- run: |
nix build -L \
.#hydraJobs.tests.functional_user \
.#hydraJobs.tests.githubFlakes \
.#hydraJobs.tests.nix-docker \
.#hydraJobs.tests.tarballFlakes \
;
flake_regressions:
needs: vm_tests
Expand Down
18 changes: 18 additions & 0 deletions doc/manual/source/installation/installing-docker.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,21 @@ $ nix build ./\#hydraJobs.dockerImage.x86_64-linux
$ docker load -i ./result/image.tar.gz
$ docker run -ti nix:2.5pre20211105
```

# Docker image with non-root Nix

If you would like to run Nix in a container under a user other than `root`,
you can build an image with a non-root single-user installation of Nix
by specifying the `uid`, `gid`, `uname`, and `gname` arguments to `docker.nix`:

```console
$ nix build --file docker.nix \
--arg uid 1000 \
--arg gid 1000 \
--argstr uname user \
--argstr gname user \
--argstr name nix-user \
--out-link nix-user.tar.gz
$ docker load -i nix-user.tar.gz
$ docker run -ti nix-user
```
50 changes: 35 additions & 15 deletions docker.nix
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
, maxLayers ? 100
, nixConf ? {}
, flake-registry ? null
, uid ? 0
, gid ? 0
, uname ? "root"
, gname ? "root"
}:
let
defaultPkgs = with pkgs; [
Expand Down Expand Up @@ -50,6 +54,15 @@ let
description = "Unprivileged account (don't use!)";
};

} // lib.optionalAttrs (uid != 0) {
"${uname}" = {
uid = uid;
shell = "${pkgs.bashInteractive}/bin/bash";
home = "/home/${uname}";
gid = gid;
groups = [ "${gname}" ];
description = "Nix user";
};
} // lib.listToAttrs (
map
(
Expand All @@ -70,6 +83,8 @@ let
root.gid = 0;
nixbld.gid = 30000;
nobody.gid = 65534;
} // lib.optionalAttrs (gid != 0) {
"${gname}".gid = gid;
};

userToPasswd = (
Expand Down Expand Up @@ -150,6 +165,8 @@ let
in
"${n} = ${vStr}") (defaultNixConf // nixConf))) + "\n";

userHome = if uid == 0 then "/root" else "/home/${uname}";

baseSystem =
let
nixpkgs = pkgs.path;
Expand Down Expand Up @@ -237,26 +254,26 @@ let
mkdir -p $out/etc/nix
cat $nixConfContentsPath > $out/etc/nix/nix.conf
mkdir -p $out/root
mkdir -p $out/nix/var/nix/profiles/per-user/root
mkdir -p $out${userHome}
mkdir -p $out/nix/var/nix/profiles/per-user/${uname}
ln -s ${profile} $out/nix/var/nix/profiles/default-1-link
ln -s $out/nix/var/nix/profiles/default-1-link $out/nix/var/nix/profiles/default
ln -s /nix/var/nix/profiles/default $out/root/.nix-profile
ln -s /nix/var/nix/profiles/default $out${userHome}/.nix-profile
ln -s ${channel} $out/nix/var/nix/profiles/per-user/root/channels-1-link
ln -s $out/nix/var/nix/profiles/per-user/root/channels-1-link $out/nix/var/nix/profiles/per-user/root/channels
ln -s ${channel} $out/nix/var/nix/profiles/per-user/${uname}/channels-1-link
ln -s $out/nix/var/nix/profiles/per-user/${uname}/channels-1-link $out/nix/var/nix/profiles/per-user/${uname}/channels
mkdir -p $out/root/.nix-defexpr
ln -s $out/nix/var/nix/profiles/per-user/root/channels $out/root/.nix-defexpr/channels
echo "${channelURL} ${channelName}" > $out/root/.nix-channels
mkdir -p $out${userHome}/.nix-defexpr
ln -s $out/nix/var/nix/profiles/per-user/${uname}/channels $out${userHome}/.nix-defexpr/channels
echo "${channelURL} ${channelName}" > $out${userHome}/.nix-channels
mkdir -p $out/bin $out/usr/bin
ln -s ${pkgs.coreutils}/bin/env $out/usr/bin/env
ln -s ${pkgs.bashInteractive}/bin/bash $out/bin/sh
'' + (lib.optionalString (flake-registry-path != null) ''
nixCacheDir="/root/.cache/nix"
nixCacheDir="${userHome}/.cache/nix"
mkdir -p $out$nixCacheDir
globalFlakeRegistryPath="$nixCacheDir/flake-registry.json"
ln -s ${flake-registry-path} $out$globalFlakeRegistryPath
Expand All @@ -268,7 +285,7 @@ let
in
pkgs.dockerTools.buildLayeredImageWithNixDb {

inherit name tag maxLayers;
inherit name tag maxLayers uid gid uname gname;

contents = [ baseSystem ];

Expand All @@ -279,25 +296,28 @@ pkgs.dockerTools.buildLayeredImageWithNixDb {
fakeRootCommands = ''
chmod 1777 tmp
chmod 1777 var/tmp
chown -R ${toString uid}:${toString gid} .${userHome}
chown -R ${toString uid}:${toString gid} nix
'';

config = {
Cmd = [ "/root/.nix-profile/bin/bash" ];
Cmd = [ "${userHome}/.nix-profile/bin/bash" ];
User = "${toString uid}:${toString gid}";
Env = [
"USER=root"
"USER=${uname}"
"PATH=${lib.concatStringsSep ":" [
"/root/.nix-profile/bin"
"${userHome}/.nix-profile/bin"
"/nix/var/nix/profiles/default/bin"
"/nix/var/nix/profiles/default/sbin"
]}"
"MANPATH=${lib.concatStringsSep ":" [
"/root/.nix-profile/share/man"
"${userHome}/.nix-profile/share/man"
"/nix/var/nix/profiles/default/share/man"
]}"
"SSL_CERT_FILE=/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt"
"GIT_SSL_CAINFO=/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt"
"NIX_SSL_CERT_FILE=/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt"
"NIX_PATH=/nix/var/nix/profiles/per-user/root/channels:/root/.nix-defexpr/channels"
"NIX_PATH=/nix/var/nix/profiles/per-user/${uname}/channels:${userHome}/.nix-defexpr/channels"
];
};

Expand Down
2 changes: 2 additions & 0 deletions tests/nixos/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ in

nix-copy = runNixOSTestFor "x86_64-linux" ./nix-copy.nix;

nix-docker = runNixOSTestFor "x86_64-linux" ./nix-docker.nix;

nssPreload = runNixOSTestFor "x86_64-linux" ./nss-preload.nix;

githubFlakes = runNixOSTestFor "x86_64-linux" ./github-flakes.nix;
Expand Down
47 changes: 47 additions & 0 deletions tests/nixos/nix-docker-test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#!/usr/bin/env bash
# docker.nix test script. Runs inside a built docker.nix container.

set -eEuo pipefail

export NIX_CONFIG='substituters = http://cache:5000?trusted=1'

cd /tmp

# Test getting a fetched derivation
test "$("$(nix-build -E '(import <nixpkgs> {}).hello')"/bin/hello)" = "Hello, world!"

# Test building a simple derivation
# shellcheck disable=SC2016
nix-build -E '
let
pkgs = import <nixpkgs> {};
in
builtins.derivation {
name = "test";
system = builtins.currentSystem;
builder = "${pkgs.bash}/bin/bash";
args = ["-c" "echo OK > $out"];
}'
test "$(cat result)" = OK

# Ensure #!/bin/sh shebang works
echo '#!/bin/sh' > ./shebang-test
echo 'echo OK' >> ./shebang-test
chmod +x ./shebang-test
test "$(./shebang-test)" = OK

# Ensure #!/usr/bin/env shebang works
echo '#!/usr/bin/env bash' > ./shebang-test
echo 'echo OK' >> ./shebang-test
chmod +x ./shebang-test
test "$(./shebang-test)" = OK

# Test nix-shell
{
echo '#!/usr/bin/env nix-shell'
echo '#! nix-shell -i bash'
echo '#! nix-shell -p hello'
echo 'hello'
} > ./nix-shell-test
chmod +x ./nix-shell-test
test "$(./nix-shell-test)" = "Hello, world!"
53 changes: 53 additions & 0 deletions tests/nixos/nix-docker.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Test the container built by ../../docker.nix.

{ lib, config, nixpkgs, hostPkgs, ... }:

let
pkgs = config.nodes.machine.nixpkgs.pkgs;

nixImage = import ../../docker.nix {
inherit (config.nodes.machine.nixpkgs) pkgs;
};
nixUserImage = import ../../docker.nix {
inherit (config.nodes.machine.nixpkgs) pkgs;
name = "nix-user";
uid = 1000;
gid = 1000;
uname = "user";
gname = "user";
};

containerTestScript = ./nix-docker-test.sh;

in {
name = "nix-docker";

nodes =
{ machine =
{ config, lib, pkgs, ... }:
{ virtualisation.diskSize = 4096;
};
cache =
{ config, lib, pkgs, ... }:
{ virtualisation.additionalPaths = [ pkgs.stdenv pkgs.hello ];
services.harmonia.enable = true;
networking.firewall.allowedTCPPorts = [ 5000 ];
};
};

testScript = { nodes }: ''
cache.wait_for_unit("harmonia.service")
machine.succeed("mkdir -p /etc/containers")
machine.succeed("""echo '{"default":[{"type":"insecureAcceptAnything"}]}' > /etc/containers/policy.json""")
machine.succeed("${pkgs.podman}/bin/podman load -i ${nixImage}")
machine.succeed("${pkgs.podman}/bin/podman run --rm nix nix --version")
machine.succeed("${pkgs.podman}/bin/podman run --rm -i nix < ${containerTestScript}")
machine.succeed("${pkgs.podman}/bin/podman load -i ${nixUserImage}")
machine.succeed("${pkgs.podman}/bin/podman run --rm nix-user nix --version")
machine.succeed("${pkgs.podman}/bin/podman run --rm -i nix-user < ${containerTestScript}")
machine.succeed("[[ $(${pkgs.podman}/bin/podman run --rm nix-user stat -c %u /nix/store) = 1000 ]]")
'';
}

0 comments on commit d8d5929

Please sign in to comment.