Merge pull request #9854 from the-sun-will-rise-tomorrow/docker-user

docker: Allow building for non-root user
This commit is contained in:
Robert Hensing 2024-11-18 14:55:51 +01:00 committed by GitHub
commit d8d59298e5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 163 additions and 17 deletions

View File

@ -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
@ -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

View File

@ -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
```

View File

@ -9,6 +9,10 @@
, maxLayers ? 100
, nixConf ? {}
, flake-registry ? null
, uid ? 0
, gid ? 0
, uname ? "root"
, gname ? "root"
}:
let
defaultPkgs = with pkgs; [
@ -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
(
@ -70,6 +83,8 @@ let
root.gid = 0;
nixbld.gid = 30000;
nobody.gid = 65534;
} // lib.optionalAttrs (gid != 0) {
"${gname}".gid = gid;
};
userToPasswd = (
@ -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;
@ -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
@ -268,7 +285,7 @@ let
in
pkgs.dockerTools.buildLayeredImageWithNixDb {
inherit name tag maxLayers;
inherit name tag maxLayers uid gid uname gname;
contents = [ baseSystem ];
@ -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"
];
};

View File

@ -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;

View File

@ -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!"

View File

@ -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 ]]")
'';
}