nixos/nix: Add support for running an unprivileged daemon (#491809)
This commit is contained in:
@@ -62,6 +62,101 @@ in
|
||||
];
|
||||
})
|
||||
(lib.mkRemovedOptionModule [ "nix" "daemonNiceLevel" ] "Consider nix.daemonCPUSchedPolicy instead.")
|
||||
{
|
||||
# Unprivileged Nix daemon
|
||||
config = lib.mkIf (cfg.daemonUser != "root") {
|
||||
assertions = [
|
||||
{
|
||||
message = ''
|
||||
The Nix daemon cannot run as the root group when not running as the root user.
|
||||
'';
|
||||
assertion = cfg.daemonGroup != "root";
|
||||
}
|
||||
{
|
||||
message = ''
|
||||
Nix must have the `local-overlay-store` experimental feature when not running as the root user.
|
||||
'';
|
||||
assertion = lib.elem "local-overlay-store" cfg.settings.experimental-features;
|
||||
}
|
||||
{
|
||||
message = ''
|
||||
Nix must have the `auto-allocate-uids` experimental feature when not running as the root user.
|
||||
'';
|
||||
assertion = lib.elem "auto-allocate-uids" cfg.settings.experimental-features;
|
||||
}
|
||||
];
|
||||
|
||||
nix.settings = {
|
||||
sandbox = true;
|
||||
|
||||
auto-allocate-uids = true;
|
||||
|
||||
# No such group would exist within the sandbox, so chowning to it would fail
|
||||
build-users-group = "";
|
||||
|
||||
# Default settings from Nix, we need to specify them here to use them in nix code though
|
||||
start-id = lib.mkDefault (832 * 1024 * 1024);
|
||||
id-count = lib.mkDefault (128 * 65536);
|
||||
};
|
||||
|
||||
systemd.services.nix-daemon = {
|
||||
# Nix assumes it should use `daemon` if it isn't root, so we have to set `NIX_REMOTE` anyway
|
||||
environment.NIX_REMOTE = "local?use-roots-daemon=true";
|
||||
serviceConfig = {
|
||||
User = cfg.daemonUser;
|
||||
Group = cfg.daemonGroup;
|
||||
|
||||
# Empty string needed to disable old Exec
|
||||
ExecStart = [
|
||||
""
|
||||
"${nixPackage}/libexec/nix-nswrapper ${toString cfg.settings.start-id} ${toString cfg.settings.id-count} ${nixPackage}/bin/nix-daemon --daemon"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
# We can't remount rw while unprivileged
|
||||
boot.nixStoreMountOpts = [
|
||||
"nodev"
|
||||
"nosuid"
|
||||
];
|
||||
|
||||
users.users."${cfg.daemonUser}" = {
|
||||
subUidRanges = [
|
||||
{
|
||||
startUid = cfg.settings.start-id;
|
||||
count = cfg.settings.id-count;
|
||||
}
|
||||
];
|
||||
subGidRanges = [
|
||||
{
|
||||
startGid = cfg.settings.start-id;
|
||||
count = cfg.settings.id-count;
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
systemd.tmpfiles.rules = [
|
||||
"d /nix/store 0755 ${config.nix.daemonUser} ${config.nix.daemonGroup} - -"
|
||||
"Z /nix/var 0755 ${config.nix.daemonUser} ${config.nix.daemonGroup} - -"
|
||||
"d /nix/var/nix/builds 0755 ${config.nix.daemonUser} ${config.nix.daemonGroup} - 7d"
|
||||
"d /nix/var/nix/daemon-socket 0755 ${config.nix.daemonUser} ${config.nix.daemonGroup} - -"
|
||||
];
|
||||
|
||||
systemd.services.nix-roots-daemon = {
|
||||
serviceConfig.ExecStart = "${config.nix.package.out}/bin/nix --extra-experimental-features nix-command store roots-daemon";
|
||||
};
|
||||
systemd.sockets.nix-roots-daemon = {
|
||||
wantedBy = [
|
||||
"nix-daemon.service"
|
||||
];
|
||||
listenStreams = [ "/nix/var/nix/gc-roots-socket/socket" ];
|
||||
unitConfig = {
|
||||
ConditionPathIsReadWrite = "/nix/var/nix/gc-roots-socket";
|
||||
RequiresMountsFor = "/nix/store";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
];
|
||||
|
||||
###### interface
|
||||
@@ -88,6 +183,24 @@ in
|
||||
'';
|
||||
};
|
||||
|
||||
daemonUser = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "root";
|
||||
description = ''
|
||||
User to use to run the Nix daemon.
|
||||
If this is not "root" then the Nix daemon will set several settings to preserve functionality.
|
||||
When setting this option, you must also set `nix.daemonGroup`.
|
||||
'';
|
||||
};
|
||||
|
||||
daemonGroup = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "root";
|
||||
description = ''
|
||||
Group to use to run the Nix daemon.
|
||||
'';
|
||||
};
|
||||
|
||||
daemonCPUSchedPolicy = lib.mkOption {
|
||||
type = lib.types.enum [
|
||||
"other"
|
||||
@@ -192,7 +305,8 @@ in
|
||||
|
||||
systemd.packages = [ nixPackage ];
|
||||
|
||||
systemd.tmpfiles.packages = [ nixPackage ];
|
||||
# The upstream Nix tmpfiles.d file assumes the daemon runs as root
|
||||
systemd.tmpfiles.packages = lib.mkIf (cfg.daemonUser == "root") [ nixPackage ];
|
||||
|
||||
systemd.sockets.nix-daemon.wantedBy = [ "sockets.target" ];
|
||||
|
||||
@@ -200,7 +314,9 @@ in
|
||||
path = [
|
||||
nixPackage
|
||||
config.programs.ssh.package
|
||||
];
|
||||
]
|
||||
# For running "newuidmap"
|
||||
++ lib.optional (cfg.daemonUser != "root") "/run/wrappers";
|
||||
|
||||
environment =
|
||||
cfg.envVars
|
||||
|
||||
@@ -1099,6 +1099,7 @@ in
|
||||
nix-channel = pkgs.callPackage ../modules/config/nix-channel/test.nix { };
|
||||
nix-config = runTest ./nix-config.nix;
|
||||
nix-daemon-firewall = runTest ./nix-daemon-firewall.nix;
|
||||
nix-daemon-unprivileged = runTest ./nix-daemon-unprivileged.nix;
|
||||
nix-ld = runTest ./nix-ld.nix;
|
||||
nix-misc = handleTest ./nix/misc.nix { };
|
||||
nix-required-mounts = runTest ./nix-required-mounts;
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
{ lib, pkgs, ... }:
|
||||
{
|
||||
name = "nix-daemon-unprivileged";
|
||||
meta.maintainers = with lib.maintainers; [ artemist ];
|
||||
|
||||
nodes.machine = {
|
||||
users.groups.nix-daemon = { };
|
||||
users.users.nix-daemon = {
|
||||
isSystemUser = true;
|
||||
group = "nix-daemon";
|
||||
};
|
||||
|
||||
nix = {
|
||||
package = pkgs.nixVersions.git;
|
||||
daemonUser = "nix-daemon";
|
||||
daemonGroup = "nix-daemon";
|
||||
settings.experimental-features = [
|
||||
"local-overlay-store"
|
||||
"auto-allocate-uids"
|
||||
];
|
||||
};
|
||||
|
||||
# Easiest way to get a file onto the machine
|
||||
environment.etc."test.nix".text = ''
|
||||
derivation {
|
||||
name = "test";
|
||||
builder = "/bin/sh";
|
||||
args = [ "-c" "echo succeeded > $out" ];
|
||||
system = "${pkgs.stdenv.hostPlatform.system}";
|
||||
}
|
||||
'';
|
||||
};
|
||||
testScript = ''
|
||||
start_all()
|
||||
machine.wait_for_unit("sockets.target")
|
||||
machine.succeed("NIX_REMOTE=daemon nix-build /etc/test.nix")
|
||||
'';
|
||||
}
|
||||
Reference in New Issue
Block a user