diff --git a/default.nix b/default.nix index dc0ccbb8..04f581d3 100644 --- a/default.nix +++ b/default.nix @@ -16,6 +16,7 @@ let ./modules/system/defaults/trackpad.nix ./modules/system/etc.nix ./modules/system/launchd.nix + ./modules/nix ./modules/nix/nix-darwin.nix ./modules/nix/nixpkgs.nix ./modules/environment diff --git a/modules/environment/default.nix b/modules/environment/default.nix index 37cd652a..3d349868 100644 --- a/modules/environment/default.nix +++ b/modules/environment/default.nix @@ -48,7 +48,7 @@ in { }; environment.variables = mkOption { - type = types.attrsOf (types.loeOf types.str); + type = types.attrsOf (types.either types.str (types.listOf types.str)); default = {}; description = '' A set of environment variables used in the global environment. @@ -71,6 +71,17 @@ in { ''; }; + environment.extraInit = mkOption { + type = types.lines; + default = ""; + description = '' + Shell script code called during global environment initialisation + after all variables and profileVariables have been set. + This code is asumed to be shell-independent, which means you should + stick to pure sh without sh word split. + ''; + }; + }; config = { diff --git a/modules/nix/default.nix b/modules/nix/default.nix new file mode 100644 index 00000000..7a8378ca --- /dev/null +++ b/modules/nix/default.nix @@ -0,0 +1,349 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.nix; + + nixbldUsers = map makeNixBuildUser (range 1 cfg.nrBuildUsers); + + nixConf = + let + # If we're using sandbox for builds, then provide /bin/sh in + # the sandbox as a bind-mount to bash. This means we also need to + # include the entire closure of bash. + sh = pkgs.stdenv.shell; + binshDeps = pkgs.writeReferencesToFile sh; + in + pkgs.runCommand "nix.conf" { extraOptions = cfg.extraOptions; } '' + extraPaths=$(for i in $(cat ${binshDeps}); do if test -d $i; then echo $i; fi; done) + cat > $out <nix.buildMachines. + ''; + }; + + daemonNiceLevel = mkOption { + type = types.int; + default = 0; + description = '' + Nix daemon process priority. This priority propagates to build processes. + 0 is the default Unix process priority, 19 is the lowest. + ''; + }; + + daemonIONice = mkOption { + type = types.bool; + default = false; + description = '' + Whether the Nix daemon process should considered to be low priority when + doing file system I/O. + ''; + }; + + buildMachines = mkOption { + type = types.listOf types.attrs; + default = []; + example = [ + { hostName = "voila.labs.cs.uu.nl"; + sshUser = "nix"; + sshKey = "/root/.ssh/id_buildfarm"; + system = "powerpc-darwin"; + maxJobs = 1; + } + { hostName = "linux64.example.org"; + sshUser = "buildfarm"; + sshKey = "/root/.ssh/id_buildfarm"; + system = "x86_64-linux"; + maxJobs = 2; + supportedFeatures = [ "kvm" ]; + mandatoryFeatures = [ "perf" ]; + } + ]; + description = '' + This option lists the machines to be used if distributed + builds are enabled (see + ). Nix will perform + derivations on those machines via SSH by copying the inputs + to the Nix store on the remote machine, starting the build, + then copying the output back to the local Nix store. Each + element of the list should be an attribute set containing + the machine's host name (hostname), the + user name to be used for the SSH connection + (sshUser), the Nix system type + (system, e.g., + "i686-linux"), the maximum number of + jobs to be run in parallel on that machine + (maxJobs), the path to the SSH private + key to be used to connect (sshKey), a + list of supported features of the machine + (supportedFeatures) and a list of + mandatory features of the machine + (mandatoryFeatures). The SSH private key + should not have a passphrase, and the corresponding public + key should be added to + ~sshUser/authorized_keys + on the remote machine. + ''; + }; + + # Environment variables for running Nix. + envVars = mkOption { + type = types.attrs; + internal = true; + default = {}; + description = "Environment variables used by Nix."; + }; + + readOnlyStore = mkOption { + type = types.bool; + default = true; + description = '' + If set, NixOS will enforce the immutability of the Nix store + by making /nix/store a read-only bind + mount. Nix will automatically make the store writable when + needed. + ''; + }; + + binaryCaches = mkOption { + type = types.listOf types.str; + default = [ https://cache.nixos.org/ ]; + description = '' + List of binary cache URLs used to obtain pre-built binaries + of Nix packages. + ''; + }; + + trustedBinaryCaches = mkOption { + type = types.listOf types.str; + default = [ ]; + example = [ http://hydra.nixos.org/ ]; + description = '' + List of binary cache URLs that non-root users can use (in + addition to those specified using + ) by passing + --option binary-caches to Nix commands. + ''; + }; + + requireSignedBinaryCaches = mkOption { + type = types.bool; + default = true; + description = '' + If enabled (the default), Nix will only download binaries from binary caches if + they are cryptographically signed with any of the keys listed in + . If disabled, signatures are neither + required nor checked, so it's strongly recommended that you use only + trustworthy caches and https to prevent man-in-the-middle attacks. + ''; + }; + + binaryCachePublicKeys = mkOption { + type = types.listOf types.str; + example = [ "hydra.nixos.org-1:CNHJZBh9K4tP3EKF6FkkgeVYsS3ohTl+oS0Qa8bezVs=" ]; + description = '' + List of public keys used to sign binary caches. If + is enabled, + then Nix will use a binary from a binary cache if and only + if it is signed by any of the keys + listed here. By default, only the key for + cache.nixos.org is included. + ''; + }; + + trustedUsers = mkOption { + type = types.listOf types.str; + default = [ "root" ]; + example = [ "root" "alice" "@wheel" ]; + description = '' + A list of names of users that have additional rights when + connecting to the Nix daemon, such as the ability to specify + additional binary caches, or to import unsigned NARs. You + can also specify groups by prefixing them with + @; for instance, + @wheel means all users in the wheel + group. + ''; + }; + + allowedUsers = mkOption { + type = types.listOf types.str; + default = [ "*" ]; + example = [ "@wheel" "@builders" "alice" "bob" ]; + description = '' + A list of names of users (separated by whitespace) that are + allowed to connect to the Nix daemon. As with + , you can specify groups by + prefixing them with @. Also, you can + allow all users by specifying *. The + default is *. Note that trusted users are + always allowed to connect. + ''; + }; + + nixPath = mkOption { + type = types.listOf types.str; + default = + [ "nixpkgs=/nix/var/nix/profiles/per-user/root/channels/nixpkgs" + "/nix/var/nix/profiles/per-user/root/channels" + "darwin=$HOME/.nix-defexpr/darwin" + "darwin-config=$HOME/.nixpkgs/darwin-configuration.nix" + ]; + description = '' + The default Nix expression search path, used by the Nix + evaluator to look up paths enclosed in angle brackets + (e.g. <nixpkgs>). + ''; + }; + + }; + + }; + + config = { + + nix.binaryCachePublicKeys = [ "cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=" ]; + + environment.etc."nix/nix.conf".source = nixConf; + + # List of machines for distributed Nix builds in the format + # expected by build-remote.pl. + environment.etc."nix/machines" = + { enable = cfg.buildMachines != []; + text = + concatMapStrings (machine: + "${if machine ? sshUser then "${machine.sshUser}@" else ""}${machine.hostName} " + + machine.system or (concatStringsSep "," machine.systems) + + " ${machine.sshKey or "-"} ${toString machine.maxJobs or 1} " + + toString (machine.speedFactor or 1) + + " " + + concatStringsSep "," (machine.mandatoryFeatures or [] ++ machine.supportedFeatures or []) + + " " + + concatStringsSep "," machine.mandatoryFeatures or [] + + "\n" + ) cfg.buildMachines; + }; + + nix.envVars = + { NIX_CONF_DIR = "/etc/nix"; + + # Enable the copy-from-other-stores substituter, which allows + # builds to be sped up by copying build results from remote + # Nix stores. To do this, mount the remote file system on a + # subdirectory of /run/nix/remote-stores. + NIX_OTHER_STORES = "/run/nix/remote-stores/*/nix"; + } + + // optionalAttrs cfg.distributedBuilds { + NIX_BUILD_HOOK = "${cfg.package}/libexec/nix/build-remote.pl"; + NIX_REMOTE_SYSTEMS = "/etc/nix/machines"; + NIX_CURRENT_LOAD = "/run/nix/current-load"; + }; + + # Set up the environment variables for running Nix. + environment.variables = cfg.envVars // + { NIX_PATH = concatStringsSep ":" cfg.nixPath; + }; + + }; +} diff --git a/modules/programs/bash.nix b/modules/programs/bash.nix index 9d7930b0..ae8e399c 100644 --- a/modules/programs/bash.nix +++ b/modules/programs/bash.nix @@ -68,6 +68,7 @@ in ${config.system.build.setAliases} ${cfg.interactiveShellInit} + ${config.environment.extraInit} # Read system-wide modifications. if test -f /etc/bash.local; then diff --git a/modules/programs/zsh.nix b/modules/programs/zsh.nix index 8c845574..8da81dbf 100644 --- a/modules/programs/zsh.nix +++ b/modules/programs/zsh.nix @@ -117,6 +117,7 @@ in ${config.system.build.setAliases} ${cfg.interactiveShellInit} + ${config.environment.extraInit} # Read system-wide modifications. if test -f /etc/zshrc.local; then diff --git a/modules/services/nix-daemon.nix b/modules/services/nix-daemon.nix index 2d64bc5e..a8e448b8 100644 --- a/modules/services/nix-daemon.nix +++ b/modules/services/nix-daemon.nix @@ -18,18 +18,6 @@ in description = "Whether to activate system at boot time."; }; - profile = mkOption { - type = types.path; - default = "/nix/var/nix/profiles/default"; - description = "Profile to use for nix and cacert."; - }; - - buildMachinesFile = mkOption { - type = types.path; - default = "/etc/nix/machines"; - description = "File containing build machines."; - }; - tempDir = mkOption { type = types.path; default = "/tmp"; @@ -41,17 +29,25 @@ in config = mkIf cfg.enable { + environment.extraInit = '' + # Set up secure multi-user builds: non-root users build through the + # Nix daemon. + if [ "$USER" != root -o ! -w /nix/var/nix/db ]; then + export NIX_REMOTE=daemon + fi + ''; + launchd.daemons.nix-daemon = { - serviceConfig.Program = "${cfg.profile}/bin/nix-daemon"; + serviceConfig.Program = "${config.nix.package}/bin/nix-daemon"; serviceConfig.KeepAlive = true; serviceConfig.ProcessType = "Background"; + serviceConfig.LowPriorityIO = config.nix.daemonIONice; + serviceConfig.Nice = config.nix.daemonNiceLevel; serviceConfig.SoftResourceLimits.NumberOfFiles = 4096; - serviceConfig.EnvironmentVariables.TMPDIR = "${cfg.tempDir}"; - serviceConfig.EnvironmentVariables.SSL_CERT_FILE = "${cfg.profile}/etc/ssl/certs/ca-bundle.crt"; - serviceConfig.EnvironmentVariables.NIX_BUILD_HOOK="${cfg.profile}/libexec/nix/build-remote.pl"; - serviceConfig.EnvironmentVariables.NIX_CURRENT_LOAD="${cfg.tempDir}/current-load"; - serviceConfig.EnvironmentVariables.NIX_REMOTE_SYSTEMS="${cfg.buildMachinesFile}"; - }; + serviceConfig.EnvironmentVariables = config.nix.envVars + // { CURL_CA_BUNDLE = "/etc/ssl/certs/ca-certificates.crt"; } + // { TMPDIR = "${cfg.tempDir}"; }; + }; }; }