Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 119 additions & 2 deletions nixos/modules/services/system/nix-daemon.nix
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,102 @@ 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} - -"
"d /nix/var/nix/gc-roots-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
Expand All @@ -88,6 +184,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"
Expand Down Expand Up @@ -192,15 +306,18 @@ 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" ];

systemd.services.nix-daemon = {
path = [
nixPackage
config.programs.ssh.package
];
]
# For running "newuidmap"
++ lib.optional (cfg.daemonUser != "root") "/run/wrappers";

environment =
cfg.envVars
Expand Down
Loading