diff --git a/nixos/modules/virtualisation/containers.nix b/nixos/modules/virtualisation/containers.nix index e1a91f7704e7..b7cb29eaa22a 100644 --- a/nixos/modules/virtualisation/containers.nix +++ b/nixos/modules/virtualisation/containers.nix @@ -139,6 +139,7 @@ let --bind="/nix/var/nix/profiles/per-container/$INSTANCE:/nix/var/nix/profiles" \ --bind="/nix/var/nix/gcroots/per-container/$INSTANCE:/nix/var/nix/gcroots" \ ${optionalString (!cfg.ephemeral) "--link-journal=try-guest"} \ + ${optionalString (cfg.unprivileged) "-U"} \ --setenv PRIVATE_NETWORK="$PRIVATE_NETWORK" \ --setenv HOST_BRIDGE="$HOST_BRIDGE" \ --setenv HOST_ADDRESS="$HOST_ADDRESS" \ @@ -238,8 +239,8 @@ let ExecReload = pkgs.writeScript "reload-container" '' #! ${pkgs.runtimeShell} -e - ${pkgs.nixos-container}/bin/nixos-container run "$INSTANCE" -- \ - bash --login -c "''${SYSTEM_PATH:-/nix/var/nix/profiles/system}/bin/switch-to-configuration test" + ${pkgs.systemd}/bin/machinectl shell "$INSTANCE" \ + ''${SYSTEM_PATH:-/nix/var/nix/profiles/system}/bin/switch-to-configuration test ''; SyslogIdentifier = "container %i"; @@ -423,6 +424,7 @@ let extraVeths = {}; additionalCapabilities = []; ephemeral = false; + unprivileged = false; allowedDevices = []; hostAddress = null; hostAddress6 = null; @@ -516,6 +518,16 @@ in ''; }; + unprivileged = mkOption { + type = types.bool; + default = false; + description = '' + Run container in unprivileged mode using private users feature of systemd-nspawn. + This option is eqvivalent of adding -U parameter to systemd-nspawn command. + See systemd-nspawn(1) man page for more information. + ''; + }; + ephemeral = mkOption { type = types.bool; default = false; diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 234609adbc09..943bf12c254a 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -48,6 +48,7 @@ in colord = handleTest ./colord.nix {}; containers-bridge = handleTest ./containers-bridge.nix {}; containers-ephemeral = handleTest ./containers-ephemeral.nix {}; + containers-unprivileged = handleTest ./containers-unprivileged.nix {}; containers-extra_veth = handleTest ./containers-extra_veth.nix {}; containers-hosts = handleTest ./containers-hosts.nix {}; containers-imperative = handleTest ./containers-imperative.nix {}; diff --git a/nixos/tests/containers-unprivileged.nix b/nixos/tests/containers-unprivileged.nix new file mode 100644 index 000000000000..2db6b7e4f022 --- /dev/null +++ b/nixos/tests/containers-unprivileged.nix @@ -0,0 +1,56 @@ +# Test for NixOS' container support. + +import ./make-test.nix ({ pkgs, ...} : { + name = "containers-unprivileged"; + + machine = { pkgs, ... }: { + virtualisation.memorySize = 768; + virtualisation.writableStore = true; + + containers.webserver = { + unprivileged = true; + privateNetwork = true; + hostAddress = "10.231.136.1"; + localAddress = "10.231.136.2"; + config = { + services.nginx = { + enable = true; + virtualHosts.localhost = { + root = (pkgs.runCommand "localhost" {} '' + mkdir "$out" + echo hello world > "$out/index.html" + ''); + }; + }; + networking.firewall.allowedTCPPorts = [ 80 ]; + }; + }; + }; + + testScript = '' + $machine->succeed("nixos-container list") =~ /webserver/ or die; + + # Start the webserver container. + $machine->succeed("nixos-container start webserver"); + + my $ip = $machine->succeed("nixos-container show-ip webserver"); + chomp $ip; + $machine->succeed("ping -n -c1 $ip"); + + # Check that container root folder is owned by a new private user + $machine->succeed('test $(stat -c "%U" /var/lib/containers/webserver) == "vu-webserver-0"'); + + # Check that webserver is working before reload + $machine->succeed("curl --fail http://$ip/ > /dev/null"); + + # Reload container + $machine->succeed('systemctl reload container@webserver'); + + # Check that webserver is working after reload + $machine->succeed("curl --fail http://$ip/ > /dev/null"); + + # Stop the container. + $machine->succeed("nixos-container stop webserver"); + $machine->fail("curl --fail --connect-timeout 2 http://$ip/ > /dev/null"); + ''; +})