diff options
author | Vladimír Čunát <vcunat@gmail.com> | 2016-03-14 11:27:15 +0100 |
---|---|---|
committer | Vladimír Čunát <vcunat@gmail.com> | 2016-03-14 11:27:15 +0100 |
commit | d6b46ecb30f1a18a4b08ed29f03d2b5a6941aec4 (patch) | |
tree | a6b6cc322e9a615bf0e8b49de927d4edfc214554 /nixos | |
parent | ab8a691d059b364c435a1871ae2ef70a578685d8 (diff) | |
parent | 55b6c1792619085dd8aacf46b94e2f33303dd58e (diff) | |
download | nixlib-d6b46ecb30f1a18a4b08ed29f03d2b5a6941aec4.tar nixlib-d6b46ecb30f1a18a4b08ed29f03d2b5a6941aec4.tar.gz nixlib-d6b46ecb30f1a18a4b08ed29f03d2b5a6941aec4.tar.bz2 nixlib-d6b46ecb30f1a18a4b08ed29f03d2b5a6941aec4.tar.lz nixlib-d6b46ecb30f1a18a4b08ed29f03d2b5a6941aec4.tar.xz nixlib-d6b46ecb30f1a18a4b08ed29f03d2b5a6941aec4.tar.zst nixlib-d6b46ecb30f1a18a4b08ed29f03d2b5a6941aec4.zip |
Merge branch 'closure-size' into p/default-outputs
Diffstat (limited to 'nixos')
202 files changed, 5764 insertions, 3040 deletions
diff --git a/nixos/default.nix b/nixos/default.nix index 6359d10c8805..5d69b79e13a6 100644 --- a/nixos/default.nix +++ b/nixos/default.nix @@ -1,20 +1,12 @@ { configuration ? import ./lib/from-env.nix "NIXOS_CONFIG" <nixos-config> , system ? builtins.currentSystem -, extraModules ? [] - # This attribute is used to specify a different nixos version, a different - # system or additional modules which might be set conditionally. -, reEnter ? false }: let - reEnterModule = { - config.nixos.path = with (import ../lib); mkIf reEnter (mkForce null); - config.nixos.configuration = configuration; - }; eval = import ./lib/eval-config.nix { inherit system; - modules = [ configuration reEnterModule ] ++ extraModules; + modules = [ configuration ]; }; inherit (eval) pkgs; @@ -22,14 +14,14 @@ let # This is for `nixos-rebuild build-vm'. vmConfig = (import ./lib/eval-config.nix { inherit system; - modules = [ configuration reEnterModule ./modules/virtualisation/qemu-vm.nix ] ++ extraModules; + modules = [ configuration ./modules/virtualisation/qemu-vm.nix ]; }).config; # This is for `nixos-rebuild build-vm-with-bootloader'. vmWithBootLoaderConfig = (import ./lib/eval-config.nix { inherit system; modules = - [ configuration reEnterModule + [ configuration ./modules/virtualisation/qemu-vm.nix { virtualisation.useBootLoader = true; } ]; @@ -38,7 +30,7 @@ let in { - inherit (eval.config.nixos.reflect) config options; + inherit (eval) config options; system = eval.config.system.build.toplevel; diff --git a/nixos/doc/manual/configuration/config-file.xml b/nixos/doc/manual/configuration/config-file.xml index b613c7f06cc8..9b240979273d 100644 --- a/nixos/doc/manual/configuration/config-file.xml +++ b/nixos/doc/manual/configuration/config-file.xml @@ -157,7 +157,7 @@ boot.kernel.sysctl."net.ipv4.tcp_keepalive_time" = 60; fileSystems."/boot" = { device = "/dev/sda1"; fsType = "ext4"; - options = "rw,data=ordered,relatime"; + options = [ "rw" "data=ordered" "relatime" ]; }; </programlisting> </para> diff --git a/nixos/doc/manual/configuration/configuration.xml b/nixos/doc/manual/configuration/configuration.xml index 1e488b59343e..caba8fb1f4ad 100644 --- a/nixos/doc/manual/configuration/configuration.xml +++ b/nixos/doc/manual/configuration/configuration.xml @@ -26,8 +26,8 @@ effect after you run <command>nixos-rebuild</command>.</para> <!-- FIXME: auto-include NixOS module docs --> <xi:include href="postgresql.xml" /> +<xi:include href="gitlab.xml" /> <xi:include href="acme.xml" /> -<xi:include href="nixos.xml" /> <!-- Apache; libvirtd virtualisation --> diff --git a/nixos/doc/manual/configuration/user-mgmt.xml b/nixos/doc/manual/configuration/user-mgmt.xml index 40362fbbb23f..631742059278 100644 --- a/nixos/doc/manual/configuration/user-mgmt.xml +++ b/nixos/doc/manual/configuration/user-mgmt.xml @@ -65,6 +65,14 @@ account named <literal>alice</literal>: <screen> $ useradd -m alice</screen> +To make all nix tools available to this new user use `su - USER` which +opens a login shell (==shell that loads the profile) for given user. +This will create the ~/.nix-defexpr symlink. So run: + +<screen> +$ su - alice -c "true"</screen> + + The flag <option>-m</option> causes the creation of a home directory for the new user, which is generally what you want. The user does not have an initial password and therefore cannot log in. A password can diff --git a/nixos/doc/manual/default.nix b/nixos/doc/manual/default.nix index be2d69a5f531..4c0e5518f1e4 100644 --- a/nixos/doc/manual/default.nix +++ b/nixos/doc/manual/default.nix @@ -1,4 +1,4 @@ -{ pkgs, options, version, revision }: +{ pkgs, options, version, revision, extraSources ? [] }: with pkgs; with pkgs.lib; @@ -17,19 +17,20 @@ let # Clean up declaration sites to not refer to the NixOS source tree. optionsList' = flip map optionsList (opt: opt // { - declarations = map (fn: stripPrefix fn) opt.declarations; + declarations = map stripAnyPrefixes opt.declarations; } // optionalAttrs (opt ? example) { example = substFunction opt.example; } // optionalAttrs (opt ? default) { default = substFunction opt.default; } // optionalAttrs (opt ? type) { type = substFunction opt.type; }); - prefix = toString ../../..; - - stripPrefix = fn: - if substring 0 (stringLength prefix) fn == prefix then - substring (stringLength prefix + 1) 1000 fn - else - fn; + # We need to strip references to /nix/store/* from options, + # including any `extraSources` if some modules came from elsewhere, + # or else the build will fail. + # + # E.g. if some `options` came from modules in ${pkgs.customModules}/nix, + # you'd need to include `extraSources = [ pkgs.customModules ]` + prefixesToStrip = map (p: "${toString p}/") ([ ../../.. ] ++ extraSources); + stripAnyPrefixes = flip (fold removePrefix) prefixesToStrip; # Convert the list of options into an XML file. optionsXML = builtins.toFile "options.xml" (builtins.toXML optionsList'); @@ -55,8 +56,8 @@ let cp -prd $sources/* . # */ chmod -R u+w . cp ${../../modules/services/databases/postgresql.xml} configuration/postgresql.xml + cp ${../../modules/services/misc/gitlab.xml} configuration/gitlab.xml cp ${../../modules/security/acme.xml} configuration/acme.xml - cp ${../../modules/misc/nixos.xml} configuration/nixos.xml ln -s ${optionsDocBook} options-db.xml echo "${version}" > version ''; diff --git a/nixos/doc/manual/development/writing-modules.xml b/nixos/doc/manual/development/writing-modules.xml index a699e74e5f62..971e586f20bd 100644 --- a/nixos/doc/manual/development/writing-modules.xml +++ b/nixos/doc/manual/development/writing-modules.xml @@ -107,12 +107,12 @@ the file system. This module declares two options that can be defined by other modules (typically the user’s <filename>configuration.nix</filename>): <option>services.locate.enable</option> (whether the database should -be updated) and <option>services.locate.period</option> (when the +be updated) and <option>services.locate.interval</option> (when the update should be done). It implements its functionality by defining two options declared by other modules: <option>systemd.services</option> (the set of all systemd services) -and <option>services.cron.systemCronJobs</option> (the list of -commands to be executed periodically by <command>cron</command>).</para> +and <option>systemd.timers</option> (the list of commands to be +executed periodically by <command>systemd</command>).</para> <example xml:id='locate-example'><title>NixOS Module for the “locate” Service</title> <programlisting> @@ -120,53 +120,59 @@ commands to be executed periodically by <command>cron</command>).</para> with lib; -let locatedb = "/var/cache/locatedb"; in - -{ - options = { - - services.locate = { - - enable = mkOption { - type = types.bool; - default = false; - description = '' - If enabled, NixOS will periodically update the database of - files used by the <command>locate</command> command. - ''; - }; - - period = mkOption { - type = types.str; - default = "15 02 * * *"; - description = '' - This option defines (in the format used by cron) when the - locate database is updated. The default is to update at - 02:15 at night every day. - ''; - }; +let + cfg = config.services.locate; +in { + options.services.locate = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + If enabled, NixOS will periodically update the database of + files used by the <command>locate</command> command. + ''; + }; + interval = mkOption { + type = types.str; + default = "02:15"; + example = "hourly"; + description = '' + Update the locate database at this interval. Updates by + default at 2:15 AM every day. + + The format is described in + <citerefentry><refentrytitle>systemd.time</refentrytitle> + <manvolnum>7</manvolnum></citerefentry>. + ''; }; + # Other options omitted for documentation }; config = { - systemd.services.update-locatedb = { description = "Update Locate Database"; path = [ pkgs.su ]; script = '' - mkdir -m 0755 -p $(dirname ${locatedb}) - exec updatedb --localuser=nobody --output=${locatedb} --prunepaths='/tmp /var/tmp /run' + mkdir -m 0755 -p $(dirname ${toString cfg.output}) + exec updatedb \ + --localuser=${cfg.localuser} \ + ${optionalString (!cfg.includeStore) "--prunepaths='/nix/store'"} \ + --output=${toString cfg.output} ${concatStringsSep " " cfg.extraFlags} ''; }; - services.cron.systemCronJobs = optional config.services.locate.enable - "${config.services.locate.period} root ${config.systemd.package}/bin/systemctl start update-locatedb.service"; - + systemd.timers.update-locatedb = mkIf cfg.enable + { description = "Update timer for locate database"; + partOf = [ "update-locatedb.service" ]; + wantedBy = [ "timers.target" ]; + timerConfig.OnCalendar = cfg.interval; + }; }; -}</programlisting> +} +</programlisting> </example> <xi:include href="option-declarations.xml" /> diff --git a/nixos/doc/manual/installation/installing.xml b/nixos/doc/manual/installation/installing.xml index 9aec57fb6d5a..7e71df28cdb3 100644 --- a/nixos/doc/manual/installation/installing.xml +++ b/nixos/doc/manual/installation/installing.xml @@ -22,7 +22,10 @@ (with empty password).</para></listitem> <listitem><para>If you downloaded the graphical ISO image, you can - run <command>start display-manager</command> to start KDE.</para></listitem> + run <command>start display-manager</command> to start KDE. If you + want to continue on the terminal, you can use + <command>loadkeys</command> to switch to your preferred keyboard layout. + (We even provide neo2 via <command>loadkeys de neo</command>!)</para></listitem> <listitem><para>The boot process should have brought up networking (check <command>ip a</command>). Networking is necessary for the diff --git a/nixos/doc/manual/man-nixos-generate-config.xml b/nixos/doc/manual/man-nixos-generate-config.xml index e4fba4a40a97..140642bc9c9c 100644 --- a/nixos/doc/manual/man-nixos-generate-config.xml +++ b/nixos/doc/manual/man-nixos-generate-config.xml @@ -165,13 +165,13 @@ look like this: fileSystems."/" = { device = "/dev/disk/by-label/nixos"; fsType = "ext3"; - options = "rw,data=ordered,relatime"; + options = [ "rw" "data=ordered" "relatime" ]; }; fileSystems."/boot" = { device = "/dev/sda1"; fsType = "ext3"; - options = "rw,errors=continue,user_xattr,acl,barrier=1,data=writeback,relatime"; + options = [ "rw" "errors=continue" "user_xattr" "acl" "barrier=1" "data=writeback" "relatime" ]; }; swapDevices = diff --git a/nixos/doc/manual/release-notes/release-notes.xml b/nixos/doc/manual/release-notes/release-notes.xml index 6ed99315a7af..2beaab00800d 100644 --- a/nixos/doc/manual/release-notes/release-notes.xml +++ b/nixos/doc/manual/release-notes/release-notes.xml @@ -9,7 +9,7 @@ <para>This section lists the release notes for each stable version of NixOS and current unstable revision.</para> -<xi:include href="rl-unstable.xml" /> +<xi:include href="rl-1603.xml" /> <xi:include href="rl-1509.xml" /> <xi:include href="rl-1412.xml" /> <xi:include href="rl-1404.xml" /> diff --git a/nixos/doc/manual/release-notes/rl-unstable.xml b/nixos/doc/manual/release-notes/rl-1603.xml index cd828dfc8887..0ede1a9ce8ed 100644 --- a/nixos/doc/manual/release-notes/rl-unstable.xml +++ b/nixos/doc/manual/release-notes/rl-1603.xml @@ -2,9 +2,9 @@ xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xi="http://www.w3.org/2001/XInclude" version="5.0" - xml:id="sec-release-unstable"> + xml:id="sec-release-16.03"> -<title>Unstable</title> +<title>Release 16.03 (“Emu”, 2016/03/??)</title> <para>In addition to numerous new and upgraded packages, this release has the following highlights:</para> @@ -12,19 +12,6 @@ has the following highlights:</para> <itemizedlist> <listitem> - <para>You can now pin a specific version of NixOS in your <filename>configuration.nix</filename> - by setting: - -<programlisting> -nixos.path = ./nixpkgs-unstable-2015-12-06/nixos; -</programlisting> - - This will make NixOS re-evaluate your configuration with the modules of - the specified NixOS version at the given path. For more details, see - <xref linkend="module-misc-nixos" /></para> - </listitem> - - <listitem> <para>Firefox and similar browsers are now <emphasis>wrapped by default</emphasis>. The package and attribute names are plain <literal>firefox</literal> or <literal>midori</literal>, etc. Backward-compatibility attributes were set up, @@ -41,7 +28,10 @@ nixos.path = ./nixpkgs-unstable-2015-12-06/nixos; <itemizedlist> <listitem><para><literal>services/monitoring/longview.nix</literal></para></listitem> + <listitem><para><literal>services/networking/pdnsd.nix</literal></para></listitem> <listitem><para><literal>services/web-apps/pump.io.nix</literal></para></listitem> + <listitem><para><literal>services/security/haka.nix</literal></para></listitem> + <listitem><para><literal>i18n/inputMethod/default.nix</literal></para></listitem> </itemizedlist> </para> @@ -145,6 +135,118 @@ nginx.override { from the ELPA, MELPA, and MELPA Stable repositories. </para> </listitem> + + <listitem> + <para>Data directory for Postfix MTA server is moved from + <filename>/var/postfix</filename> to <filename>/var/lib/postfix</filename>. + Old configurations are migrated automatically. <literal>service.postfix</literal> + module has also received many improvements, such as correct directories' access + rights, new <literal>aliasFiles</literal> and <literal>mapFiles</literal> + options and more.</para> + </listitem> + + <listitem> + <para>Filesystem options should now be configured as a list of strings, not + a comma-separated string. The old style will continue to work, but print a + warning, until the 16.09 release. An example of the new style: + +<programlisting> +fileSystems."/example" = { + device = "/dev/sdc"; + fsType = "btrfs"; + options = [ "noatime" "compress=lzo" "space_cache" "autodefrag" ]; +}; +</programlisting> + </para> + </listitem> + + <listitem> + <para>CUPS, installed by <literal>services.printing</literal> module, now + has its data directory in <filename>/var/lib/cups</filename>. Old + configurations from <filename>/etc/cups</filename> are moved there + automatically, but there might be problems. Also configuration options + <literal>services.printing.cupsdConf</literal> and + <literal>services.printing.cupsdFilesConf</literal> were removed + because they had been allowing one to override configuration variables + required for CUPS to work at all on NixOS. For most use cases, + <literal>services.printing.extraConf</literal> and new option + <literal>services.printing.extraFilesConf</literal> should be enough; + if you encounter a situation when they are not, please file a bug.</para> + + <para>There are also Gutenprint improvements; in particular, a new option + <literal>services.printing.gutenprint</literal> is added to enable automatic + updating of Gutenprint PPMs; it's greatly recommended to enable it instead + of adding <literal>gutenprint</literal> to the <literal>drivers</literal> list. + </para> + </listitem> + + <listitem> + <para><literal>services.xserver.vaapiDrivers</literal> has been removed. Use + <literal>services.hardware.opengl.extraPackages{,32}</literal> instead. You can + also specify VDPAU drivers there.</para> + </listitem> + + <listitem> + <para> + <literal>programs.ibus</literal> moved to <literal>i18n.inputMethod.ibus</literal>. + The option <literal>programs.ibus.plugins</literal> changed to <literal>i18n.inputMethod.ibus.engines</literal> + and the option to enable ibus changed from <literal>programs.ibus.enable</literal> to + <literal>i18n.inputMethod.enabled</literal>. + <literal>i18n.inputMethod.enabled</literal> should be set to the used input method name, + <literal>"ibus"</literal> for ibus. + An example of the new style: + +<programlisting> +i18n.inputMethod.enabled = "ibus"; +i18n.inputMethod.ibus.engines = with pkgs.ibus-engines; [ anthy mozc ]; +</programlisting> + +That is equivalent to the old version: + +<programlisting> +programs.ibus.enable = true; +programs.ibus.plugins = with pkgs; [ ibus-anthy mozc ]; +</programlisting> + + </para> + </listitem> + + <listitem> + <para><literal>services.udev.extraRules</literal> option now writes rules + to <filename>99-local.rules</filename> instead of <filename>10-local.rules</filename>. + This makes all the user rules apply after others, so their results wouldn't be + overriden by anything else.</para> + </listitem> + + <listitem> + <para>Large parts of the <literal>services.gitlab</literal> module has been + been rewritten. There are new configuration options available. The + <literal>stateDir</literal> option was renamned to + <literal>statePath</literal> and the <literal>satellitesDir</literal> option + was removed. Please review the currently available options.</para> + </listitem> + + <listitem> + <para> + The option <option>services.nsd.zones.<name>.data</option> no + longer interpret the dollar sign ($) as a shell variable, as such it + should not be escaped anymore. Thus the following zone data: + </para> + <programlisting> +\$ORIGIN example.com. +\$TTL 1800 +@ IN SOA ns1.vpn.nbp.name. admin.example.com. ( + </programlisting> + <para> + Should modified to look like the actual file expected by nsd: + </para> + <programlisting> +$ORIGIN example.com. +$TTL 1800 +@ IN SOA ns1.vpn.nbp.name. admin.example.com. ( + </programlisting> + </listitem> + </itemizedlist> @@ -158,6 +260,17 @@ nginx.override { <command>nix-shell</command> (without installing anything). </para> </listitem> + <listitem> + <para><literal>ejabberd</literal> module is brought back and now works on + NixOS.</para> + </listitem> + + <listitem> + <para>Input method support was improved. New NixOS modules (fcitx, nabi and uim), + fcitx engines (chewing, hangul, m17n, mozc and table-other) and ibus engines (hangul and m17n) + have been added.</para> + </listitem> + </itemizedlist></para> </section> diff --git a/nixos/lib/make-iso9660-image.nix b/nixos/lib/make-iso9660-image.nix index b2409c6006bc..21c9cca316d1 100644 --- a/nixos/lib/make-iso9660-image.nix +++ b/nixos/lib/make-iso9660-image.nix @@ -22,7 +22,7 @@ , # Whether this should be an efi-bootable El-Torito CD. efiBootable ? false -, # Wheter this should be an hybrid CD (bootable from USB as well as CD). +, # Whether this should be an hybrid CD (bootable from USB as well as CD). usbBootable ? false , # The path (in the ISO file system) of the boot image. diff --git a/nixos/modules/config/fonts/fontconfig.nix b/nixos/modules/config/fonts/fontconfig.nix index c027796d5476..1eaebe4b2bbd 100644 --- a/nixos/modules/config/fonts/fontconfig.nix +++ b/nixos/modules/config/fonts/fontconfig.nix @@ -129,6 +129,14 @@ with lib; }; + cache32Bit = mkOption { + default = false; + type = types.bool; + description = '' + Generate system fonts cache for 32-bit applications. + ''; + }; + }; }; @@ -231,12 +239,19 @@ with lib; "${pkgs.fontconfig.out}/etc/fonts/fonts.conf"; environment.etc."fonts/${pkgs.fontconfig.configVersion}/conf.d/00-nixos.conf".text = - '' + let + cache = fontconfig: pkgs.makeFontsCache { inherit fontconfig; fontDirectories = config.fonts.fonts; }; + in '' <?xml version='1.0'?> <!DOCTYPE fontconfig SYSTEM 'fonts.dtd'> <fontconfig> <!-- Font directories --> ${concatStringsSep "\n" (map (font: "<dir>${font}</dir>") config.fonts.fonts)} + <!-- Pre-generated font caches --> + <cachedir>${cache pkgs.fontconfig}</cachedir> + ${optionalString (pkgs.stdenv.isx86_64 && config.fonts.fontconfig.cache32Bit) '' + <cachedir>${cache pkgs.pkgsi686Linux.fontconfig}</cachedir> + ''} </fontconfig> ''; diff --git a/nixos/modules/config/gtk-exe-env.nix b/nixos/modules/config/gtk-exe-env.nix deleted file mode 100644 index b565072e3a71..000000000000 --- a/nixos/modules/config/gtk-exe-env.nix +++ /dev/null @@ -1,41 +0,0 @@ -{ config, pkgs, lib, ... }: - -{ - imports = [ - ]; - - options = { - gtkPlugins = lib.mkOption { - type = lib.types.listOf lib.types.path; - default = []; - description = '' - Plugin packages for GTK+ such as input methods. - ''; - }; - }; - - config = { - environment.variables = if builtins.length config.gtkPlugins > 0 - then - let - paths = [ pkgs.gtk2 pkgs.gtk3 ] ++ config.gtkPlugins; - env = pkgs.buildEnv { - name = "gtk-exe-env"; - - inherit paths; - - postBuild = lib.concatStringsSep "\n" - (map (d: d.gtkExeEnvPostBuild or "") paths); - - ignoreCollisions = true; - }; - in { - GTK_EXE_PREFIX = builtins.toString env; - GTK_PATH = [ - "${env}/lib/gtk-2.0" - "${env}/lib/gtk-3.0" - ]; - } - else {}; - }; -} diff --git a/nixos/modules/config/networking.nix b/nixos/modules/config/networking.nix index cbda34348930..0c4f4cbfa5c6 100644 --- a/nixos/modules/config/networking.nix +++ b/nixos/modules/config/networking.nix @@ -39,6 +39,17 @@ in ''; }; + networking.dnsExtensionMechanism = lib.mkOption { + type = types.bool; + default = false; + description = '' + Enable the <code>edns0</code> option in <filename>resolv.conf</filename>. With + that option set, <code>glibc</code> supports use of the extension mechanisms for + DNS (EDNS) specified in RFC 2671. The most popular user of that feature is DNSSEC, + which does not work without it. + ''; + }; + networking.extraResolvconfConf = lib.mkOption { type = types.lines; default = ""; @@ -162,7 +173,10 @@ in libc_restart='${pkgs.systemd}/bin/systemctl try-restart --no-block nscd.service 2> /dev/null' '' + optionalString cfg.dnsSingleRequest '' # only send one DNS request at a time - resolv_conf_options='single-request' + resolv_conf_options+=' single-request' + '' + optionalString cfg.dnsExtensionMechanism '' + # enable extension mechanisms for DNS + resolv_conf_options+=' edns0' '' + optionalString hasLocalResolver '' # This hosts runs a full-blown DNS resolver. name_servers='127.0.0.1' diff --git a/nixos/modules/config/qt-plugin-env.nix b/nixos/modules/config/qt-plugin-env.nix deleted file mode 100644 index c59865604165..000000000000 --- a/nixos/modules/config/qt-plugin-env.nix +++ /dev/null @@ -1,37 +0,0 @@ -{ config, pkgs, lib, ... }: - -{ - imports = [ - ]; - - options = { - qtPlugins = lib.mkOption { - type = lib.types.listOf lib.types.path; - default = []; - description = '' - Plugin packages for Qt such as input methods. - ''; - }; - }; - - config = { - environment.variables = if builtins.length config.qtPlugins > 0 - then - let - paths = [ pkgs.qt48 ] ++ config.qtPlugins; - env = pkgs.buildEnv { - name = "qt-plugin-env"; - - inherit paths; - - postBuild = lib.concatStringsSep "\n" - (map (d: d.qtPluginEnvPostBuild or "") paths); - - ignoreCollisions = true; - }; - in { - QT_PLUGIN_PATH = [ (builtins.toString env) ]; - } - else {}; - }; -} diff --git a/nixos/modules/config/vpnc.nix b/nixos/modules/config/vpnc.nix index 68d755232ebe..c7ac1b3530e1 100644 --- a/nixos/modules/config/vpnc.nix +++ b/nixos/modules/config/vpnc.nix @@ -17,16 +17,16 @@ in services = mkOption { type = types.attrsOf types.str; default = {}; - example = { - test = - '' - IPSec gateway 192.168.1.1 - IPSec ID someID - IPSec secret secretKey - Xauth username name - Xauth password pass - ''; - }; + example = literalExample '' + { test = ''' + IPSec gateway 192.168.1.1 + IPSec ID someID + IPSec secret secretKey + Xauth username name + Xauth password pass + '''; + } + ''; description = '' The names of cisco VPNs and their associated definitions diff --git a/nixos/modules/config/zram.nix b/nixos/modules/config/zram.nix index 22b74847f871..019932b04e8d 100644 --- a/nixos/modules/config/zram.nix +++ b/nixos/modules/config/zram.nix @@ -98,11 +98,9 @@ in script = '' set -u set -o pipefail - - PATH=${pkgs.procps}/bin:${pkgs.gnugrep}/bin:${pkgs.gnused}/bin - + # Calculate memory to use for zram - totalmem=$(free | grep -e "^Mem:" | sed -e 's/^Mem: *//' -e 's/ *.*//') + totalmem=$(${pkgs.gnugrep}/bin/grep 'MemTotal: ' /proc/meminfo | ${pkgs.gawk}/bin/awk '{print $2}') mem=$(((totalmem * ${toString cfg.memoryPercent} / 100 / ${toString cfg.numDevices}) * 1024)) echo $mem > /sys/class/block/${dev}/disksize diff --git a/nixos/modules/hardware/all-firmware.nix b/nixos/modules/hardware/all-firmware.nix index 1a04baef1930..d0d481f72a40 100644 --- a/nixos/modules/hardware/all-firmware.nix +++ b/nixos/modules/hardware/all-firmware.nix @@ -22,7 +22,7 @@ with lib; ###### implementation config = mkIf config.hardware.enableAllFirmware { - hardware.firmware = [ pkgs.firmwareLinuxNonfree ]; + hardware.firmware = [ pkgs.firmwareLinuxNonfree pkgs.intel2200BGFirmware ]; }; } diff --git a/nixos/modules/hardware/network/intel-2200bg.nix b/nixos/modules/hardware/network/intel-2200bg.nix index 1b70057d135b..17b973474c93 100644 --- a/nixos/modules/hardware/network/intel-2200bg.nix +++ b/nixos/modules/hardware/network/intel-2200bg.nix @@ -23,7 +23,7 @@ config = lib.mkIf config.networking.enableIntel2200BGFirmware { - hardware.enableAllFirmware = true; + hardware.firmware = [ pkgs.intel2200BGFirmware ]; }; diff --git a/nixos/modules/hardware/opengl.nix b/nixos/modules/hardware/opengl.nix index 7693bd5bb453..d3b146be6b33 100644 --- a/nixos/modules/hardware/opengl.nix +++ b/nixos/modules/hardware/opengl.nix @@ -10,7 +10,7 @@ let videoDrivers = config.services.xserver.videoDrivers; - makePackage = p: p.buildEnv { + makePackage = p: pkgs.buildEnv { name = "mesa-drivers+txc-${p.mesa_drivers.version}"; paths = [ p.mesa_drivers @@ -19,6 +19,16 @@ let ]; }; + package = pkgs.buildEnv { + name = "opengl-drivers"; + paths = [ cfg.package ] ++ cfg.extraPackages; + }; + + package32 = pkgs.buildEnv { + name = "opengl-drivers-32bit"; + paths = [ cfg.package32 ] ++ cfg.extraPackages32; + }; + in { @@ -75,11 +85,32 @@ in internal = true; description = '' The package that provides the 32-bit OpenGL implementation on - 64-bit systems. Used when <option>driSupport32Bit</option> is + 64-bit systems. Used when <option>driSupport32Bit</option> is set. ''; }; + hardware.opengl.extraPackages = mkOption { + type = types.listOf types.package; + default = []; + example = literalExample "with pkgs; [ vaapiIntel libvdpau-va-gl vaapiVdpau ]"; + description = '' + Additional packages to add to OpenGL drivers. This can be used + to add additional VA-API/VDPAU drivers. + ''; + }; + + hardware.opengl.extraPackages32 = mkOption { + type = types.listOf types.package; + default = []; + example = literalExample "with pkgs; [ vaapiIntel libvdpau-va-gl vaapiVdpau ]"; + description = '' + Additional packages to add to 32-bit OpenGL drivers on + 64-bit systems. Used when <option>driSupport32Bit</option> is + set. This can be used to add additional VA-API/VDPAU drivers. + ''; + }; + }; config = mkIf cfg.enable { @@ -91,11 +122,11 @@ in system.activationScripts.setup-opengl = '' - ln -sfn ${cfg.package} /run/opengl-driver + ln -sfn ${package} /run/opengl-driver ${if pkgs.stdenv.isi686 then '' ln -sfn opengl-driver /run/opengl-driver-32 '' else if cfg.driSupport32Bit then '' - ln -sfn ${cfg.package32} /run/opengl-driver-32 + ln -sfn ${package32} /run/opengl-driver-32 '' else '' rm -f /run/opengl-driver-32 ''} diff --git a/nixos/modules/hardware/video/webcam/facetimehd.nix b/nixos/modules/hardware/video/webcam/facetimehd.nix new file mode 100644 index 000000000000..b35709763b90 --- /dev/null +++ b/nixos/modules/hardware/video/webcam/facetimehd.nix @@ -0,0 +1,45 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.hardware.facetimehd; + + kernelPackages = config.boot.kernelPackages; + +in + +{ + + options.hardware.facetimehd.enable = mkEnableOption "facetimehd kernel module"; + + config = mkIf cfg.enable { + + assertions = singleton { + assertion = versionAtLeast kernelPackages.kernel.version "3.19"; + message = "facetimehd is not supported for kernels older than 3.19"; + }; + + boot.kernelModules = [ "facetimehd" ]; + + boot.blacklistedKernelModules = [ "bdc_pci" ]; + + boot.extraModulePackages = [ kernelPackages.facetimehd ]; + + hardware.firmware = [ pkgs.facetimehd-firmware ]; + + # unload module during suspend/hibernate as it crashes the whole system + powerManagement.powerDownCommands = '' + ${pkgs.module_init_tools}/bin/rmmod -f facetimehd + ''; + + # and load it back on resume + powerManagement.resumeCommands = '' + export MODULE_DIR=/run/current-system/kernel-modules/lib/modules + ${pkgs.module_init_tools}/bin/modprobe -v facetimehd + ''; + + }; + +} diff --git a/nixos/modules/i18n/inputMethod/default.nix b/nixos/modules/i18n/inputMethod/default.nix new file mode 100644 index 000000000000..7e6a25bfb084 --- /dev/null +++ b/nixos/modules/i18n/inputMethod/default.nix @@ -0,0 +1,29 @@ +{ config, pkgs, lib, ... }: + +with lib; + +{ + options = { + i18n.inputMethod = { + enabled = mkOption { + type = types.nullOr (types.enum [ "ibus" "fcitx" "nabi" "uim" ]); + default = null; + example = "fcitx"; + description = '' + Select the enabled input method. Input methods is a software to input symbols that are not available on standard input devices. + + Input methods are specially used to input Chinese, Japanese and Korean characters. + + Currently the following input methods are available in NixOS: + + <itemizedlist> + <listitem><para>ibus: The intelligent input bus, extra input engines can be added using <literal>i18n.inputMethod.ibus.engines</literal>.</para></listitem> + <listitem><para>fcitx: A customizable lightweight input method, extra input engines can be added using <literal>i18n.inputMethod.fcitx.engines</literal>.</para></listitem> + <listitem><para>nabi: A Korean input method based on XIM. Nabi doesn't support Qt 5.</para></listitem> + <listitem><para>uim: The universal input method, is a library with a XIM bridge. uim mainly support Chinese, Japanese and Korean.</para></listitem> + </itemizedlist> + ''; + }; + }; + }; +} diff --git a/nixos/modules/i18n/inputMethod/fcitx.nix b/nixos/modules/i18n/inputMethod/fcitx.nix new file mode 100644 index 000000000000..8e31743504f1 --- /dev/null +++ b/nixos/modules/i18n/inputMethod/fcitx.nix @@ -0,0 +1,44 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let + cfg = config.i18n.inputMethod.fcitx; + fcitxPackage = pkgs.fcitx-with-plugins.override { plugins = cfg.engines; }; + fcitxEngine = types.package // { + name = "fcitx-engine"; + check = x: (lib.types.package.check x) && (attrByPath ["meta" "isFcitxEngine"] false x); + }; +in +{ + options = { + + i18n.inputMethod.fcitx = { + engines = mkOption { + type = with types; listOf fcitxEngine; + default = []; + example = literalExample "with pkgs.fcitx-engines; [ mozc hangul ]"; + description = + let + engines = + lib.concatStringsSep ", " + (map (name: "<literal>${name}</literal>") + (lib.attrNames pkgs.fcitx-engines)); + in + "Enabled Fcitx engines. Available engines are: ${engines}."; + }; + }; + + }; + + config = mkIf (config.i18n.inputMethod.enabled == "fcitx") { + environment.systemPackages = [ fcitxPackage ]; + + environment.variables = { + GTK_IM_MODULE = "fcitx"; + QT_IM_MODULE = "fcitx"; + XMODIFIERS = "@im=fcitx"; + }; + services.xserver.displayManager.sessionCommands = "${fcitxPackage}/bin/fcitx"; + }; +} diff --git a/nixos/modules/i18n/inputMethod/ibus.nix b/nixos/modules/i18n/inputMethod/ibus.nix new file mode 100644 index 000000000000..bb80f43634d3 --- /dev/null +++ b/nixos/modules/i18n/inputMethod/ibus.nix @@ -0,0 +1,55 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let + cfg = config.i18n.inputMethod.ibus; + ibusPackage = pkgs.ibus-with-plugins.override { plugins = cfg.engines; }; + ibusEngine = types.package // { + name = "ibus-engine"; + check = x: (lib.types.package.check x) && (attrByPath ["meta" "isIbusEngine"] false x); + }; + + ibusAutostart = pkgs.writeTextFile { + name = "autostart-ibus-daemon"; + destination = "/etc/xdg/autostart/ibus-daemon.desktop"; + text = '' + [Desktop Entry] + Name=IBus + Type=Application + Exec=${ibusPackage}/bin/ibus-daemon --daemonize --xim --cache=refresh + ''; + }; +in +{ + options = { + i18n.inputMethod.ibus = { + engines = mkOption { + type = with types; listOf ibusEngine; + default = []; + example = literalExample "with pkgs.ibus-engines; [ mozc hangul ]"; + description = + let + engines = + lib.concatStringsSep ", " + (map (name: "<literal>${name}</literal>") + (lib.attrNames pkgs.ibus-engines)); + in + "Enabled IBus engines. Available engines are: ${engines}."; + }; + }; + }; + + config = mkIf (config.i18n.inputMethod.enabled == "ibus") { + # Without dconf enabled it is impossible to use IBus + environment.systemPackages = with pkgs; [ + ibusPackage ibus-qt gnome3.dconf ibusAutostart + ]; + + environment.variables = { + GTK_IM_MODULE = "ibus"; + QT_IM_MODULE = "ibus"; + XMODIFIERS = "@im=ibus"; + }; + }; +} diff --git a/nixos/modules/i18n/inputMethod/nabi.nix b/nixos/modules/i18n/inputMethod/nabi.nix new file mode 100644 index 000000000000..c6708365effa --- /dev/null +++ b/nixos/modules/i18n/inputMethod/nabi.nix @@ -0,0 +1,16 @@ +{ config, pkgs, lib, ... }: + +with lib; +{ + config = mkIf (config.i18n.inputMethod.enabled == "nabi") { + environment.systemPackages = [ pkgs.nabi ]; + + environment.variables = { + GTK_IM_MODULE = "nabi"; + QT_IM_MODULE = "nabi"; + XMODIFIERS = "@im=nabi"; + }; + + services.xserver.displayManager.sessionCommands = "${pkgs.nabi}/bin/nabi &"; + }; +} diff --git a/nixos/modules/i18n/inputMethod/uim.nix b/nixos/modules/i18n/inputMethod/uim.nix new file mode 100644 index 000000000000..f8a3e560656d --- /dev/null +++ b/nixos/modules/i18n/inputMethod/uim.nix @@ -0,0 +1,37 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let + cfg = config.i18n.inputMethod.uim; +in +{ + options = { + + i18n.inputMethod.uim = { + toolbar = mkOption { + type = types.enum [ "gtk" "gtk3" "gtk-systray" "gtk3-systray" "qt4" ]; + default = "gtk"; + example = "gtk-systray"; + description = '' + selected UIM toolbar. + ''; + }; + }; + + }; + + config = mkIf (config.i18n.inputMethod.enabled == "uim") { + environment.systemPackages = [ pkgs.uim ]; + + environment.variables = { + GTK_IM_MODULE = "uim"; + QT_IM_MODULE = "uim"; + XMODIFIERS = "@im=uim"; + }; + services.xserver.displayManager.sessionCommands = '' + ${pkgs.uim}/bin/uim-xim & + ${pkgs.uim}/bin/uim-toolbar-${cfg.toolbar} & + ''; + }; +} diff --git a/nixos/modules/installer/cd-dvd/installation-cd-graphical-gnome.nix b/nixos/modules/installer/cd-dvd/installation-cd-graphical-gnome.nix new file mode 100644 index 000000000000..5725938465f5 --- /dev/null +++ b/nixos/modules/installer/cd-dvd/installation-cd-graphical-gnome.nix @@ -0,0 +1,78 @@ +# This module defines a NixOS installation CD that contains X11 and +# GNOME 3. + +{ config, lib, pkgs, ... }: + +with lib; + +{ + imports = [ ./installation-cd-base.nix ]; + + services.xserver = { + enable = true; + # GDM doesn't start in virtual machines with ISO + displayManager.slim = { + enable = true; + defaultUser = "root"; + autoLogin = true; + }; + desktopManager.gnome3 = { + enable = true; + extraGSettingsOverrides = '' + [org.gnome.desktop.background] + show-desktop-icons=true + + [org.gnome.nautilus.desktop] + trash-icon-visible=false + volumes-visible=false + home-icon-visible=false + network-icon-visible=false + ''; + + extraGSettingsOverridePackages = [ pkgs.gnome3.nautilus ]; + }; + }; + + environment.systemPackages = + [ # Include gparted for partitioning disks. + pkgs.gparted + + # Include some editors. + pkgs.vim + pkgs.bvi # binary editor + pkgs.joe + + pkgs.glxinfo + ]; + + # Don't start the X server by default. + services.xserver.autorun = mkForce false; + + # Auto-login as root. + services.xserver.displayManager.gdm.autoLogin = { + enable = true; + user = "root"; + }; + + system.activationScripts.installerDesktop = let + # Must be executable + desktopFile = pkgs.writeScript "nixos-manual.desktop" '' + [Desktop Entry] + Version=1.0 + Type=Link + Name=NixOS Manual + URL=${config.system.build.manual.manual}/share/doc/nixos/index.html + Icon=system-help + ''; + + # use cp and chmod +x, we must be sure the apps are in the nix store though + in '' + mkdir -p /root/Desktop + ln -sfT ${desktopFile} /root/Desktop/nixos-manual.desktop + cp ${pkgs.gnome3.gnome_terminal}/share/applications/gnome-terminal.desktop /root/Desktop/gnome-terminal.desktop + chmod a+rx /root/Desktop/gnome-terminal.desktop + cp ${pkgs.gparted}/share/applications/gparted.desktop /root/Desktop/gparted.desktop + chmod a+rx /root/Desktop/gparted.desktop + ''; + +} diff --git a/nixos/modules/installer/cd-dvd/installation-cd-graphical-new-kernel.nix b/nixos/modules/installer/cd-dvd/installation-cd-graphical-kde-new-kernel.nix index 506b9292b01e..a4bcd7079a4f 100644 --- a/nixos/modules/installer/cd-dvd/installation-cd-graphical-new-kernel.nix +++ b/nixos/modules/installer/cd-dvd/installation-cd-graphical-kde-new-kernel.nix @@ -1,7 +1,7 @@ { config, pkgs, ... }: { - imports = [ ./installation-cd-graphical.nix ]; + imports = [ ./installation-cd-graphical-kde.nix ]; boot.kernelPackages = pkgs.linuxPackages_latest; } diff --git a/nixos/modules/installer/cd-dvd/installation-cd-graphical.nix b/nixos/modules/installer/cd-dvd/installation-cd-graphical-kde.nix index d14768bc1079..d14768bc1079 100644 --- a/nixos/modules/installer/cd-dvd/installation-cd-graphical.nix +++ b/nixos/modules/installer/cd-dvd/installation-cd-graphical-kde.nix diff --git a/nixos/modules/installer/cd-dvd/iso-image.nix b/nixos/modules/installer/cd-dvd/iso-image.nix index 248b0f00283c..5702e2d9a1e5 100644 --- a/nixos/modules/installer/cd-dvd/iso-image.nix +++ b/nixos/modules/installer/cd-dvd/iso-image.nix @@ -249,7 +249,7 @@ in fileSystems."/" = { fsType = "tmpfs"; - options = "mode=0755"; + options = [ "mode=0755" ]; }; # Note that /dev/root is a symlink to the actual root device @@ -266,20 +266,20 @@ in fileSystems."/nix/.ro-store" = { fsType = "squashfs"; device = "/iso/nix-store.squashfs"; - options = "loop"; + options = [ "loop" ]; neededForBoot = true; }; fileSystems."/nix/.rw-store" = { fsType = "tmpfs"; - options = "mode=0755"; + options = [ "mode=0755" ]; neededForBoot = true; }; fileSystems."/nix/store" = { fsType = "unionfs-fuse"; device = "unionfs"; - options = "allow_other,cow,nonempty,chroot=/mnt-root,max_files=32768,hide_meta_files,dirs=/nix/.rw-store=rw:/nix/.ro-store=ro"; + options = [ "allow_other" "cow" "nonempty" "chroot=/mnt-root" "max_files=32768" "hide_meta_files" "dirs=/nix/.rw-store=rw:/nix/.ro-store=ro" ]; }; boot.initrd.availableKernelModules = [ "squashfs" "iso9660" "usb-storage" ]; diff --git a/nixos/modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix b/nixos/modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix index 15e22fb50d48..957a8ff9ce6d 100644 --- a/nixos/modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix +++ b/nixos/modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix @@ -23,7 +23,7 @@ in boot.loader.generic-extlinux-compatible.enable = true; boot.kernelPackages = pkgs.linuxPackages_latest; - boot.kernelParams = ["console=ttyS0,115200n8" "console=ttyAMA0,115200n8" "console=tty0"]; + boot.kernelParams = ["console=ttyS0,115200n8" "console=ttymxc0,115200n8" "console=ttyAMA0,115200n8" "console=tty0"]; # FIXME: this probably should be in installation-device.nix users.extraUsers.root.initialHashedPassword = ""; diff --git a/nixos/modules/installer/cd-dvd/sd-image.nix b/nixos/modules/installer/cd-dvd/sd-image.nix index 12b4f3045614..9eba542d8c91 100644 --- a/nixos/modules/installer/cd-dvd/sd-image.nix +++ b/nixos/modules/installer/cd-dvd/sd-image.nix @@ -30,7 +30,7 @@ in bootSize = mkOption { type = types.int; - default = 128; + default = 120; description = '' Size of the /boot partition, in megabytes. ''; @@ -66,10 +66,10 @@ in buildInputs = with pkgs; [ dosfstools e2fsprogs mtools libfaketime utillinux ]; buildCommand = '' - # Create the image file sized to fit /boot and /, plus 4M of slack + # Create the image file sized to fit /boot and /, plus 20M of slack rootSizeBlocks=$(du -B 512 --apparent-size ${rootfsImage} | awk '{ print $1 }') bootSizeBlocks=$((${toString config.sdImage.bootSize} * 1024 * 1024 / 512)) - imageSize=$((rootSizeBlocks * 512 + bootSizeBlocks * 512 + 4096 * 1024)) + imageSize=$((rootSizeBlocks * 512 + bootSizeBlocks * 512 + 20 * 1024 * 1024)) truncate -s $imageSize $out # type=b is 'W95 FAT32', type=83 is 'Linux'. @@ -77,8 +77,8 @@ in label: dos label-id: 0x2178694e - start=1M, size=$bootSizeBlocks, type=b, bootable - type=83 + start=8M, size=$bootSizeBlocks, type=b, bootable + start=${toString (8 + config.sdImage.bootSize)}M, type=83 EOF # Copy the rootfs into the SD image diff --git a/nixos/modules/installer/cd-dvd/system-tarball-sheevaplug.nix b/nixos/modules/installer/cd-dvd/system-tarball-sheevaplug.nix index de0cc604bf3b..7badfcb8df22 100644 --- a/nixos/modules/installer/cd-dvd/system-tarball-sheevaplug.nix +++ b/nixos/modules/installer/cd-dvd/system-tarball-sheevaplug.nix @@ -86,8 +86,7 @@ in system.boot.loader.kernelFile = "uImage"; boot.initrd.availableKernelModules = - [ "mvsdio" "mmc_block" "reiserfs" "ext3" "ums-cypress" "rtc_mv" - "ext4" ]; + [ "mvsdio" "reiserfs" "ext3" "ums-cypress" "rtc_mv" "ext4" ]; boot.postBootCommands = '' diff --git a/nixos/modules/installer/cd-dvd/system-tarball.nix b/nixos/modules/installer/cd-dvd/system-tarball.nix index c24fe97fba46..90e9b98a4575 100644 --- a/nixos/modules/installer/cd-dvd/system-tarball.nix +++ b/nixos/modules/installer/cd-dvd/system-tarball.nix @@ -43,7 +43,7 @@ in # so that we don't need to know its device. fileSystems = [ ]; - # boot.initrd.availableKernelModules = [ "mvsdio" "mmc_block" "reiserfs" "ext3" "ext4" ]; + # boot.initrd.availableKernelModules = [ "mvsdio" "reiserfs" "ext3" "ext4" ]; # boot.initrd.kernelModules = [ "rtc_mv" ]; diff --git a/nixos/modules/installer/tools/auto-upgrade.nix b/nixos/modules/installer/tools/auto-upgrade.nix index ca51de0fb8c7..79ccb5c3d18a 100644 --- a/nixos/modules/installer/tools/auto-upgrade.nix +++ b/nixos/modules/installer/tools/auto-upgrade.nix @@ -74,7 +74,7 @@ let cfg = config.system.autoUpgrade; in serviceConfig.Type = "oneshot"; environment = config.nix.envVars // - { inherit (config.environment.sessionVariables) NIX_PATH SSL_CERT_FILE; + { inherit (config.environment.sessionVariables) NIX_PATH; HOME = "/root"; }; diff --git a/nixos/modules/installer/tools/nixos-generate-config.pl b/nixos/modules/installer/tools/nixos-generate-config.pl index c590c4cde3f0..ec880e084726 100644 --- a/nixos/modules/installer/tools/nixos-generate-config.pl +++ b/nixos/modules/installer/tools/nixos-generate-config.pl @@ -148,7 +148,7 @@ sub pciCheck { $device eq "0x4331" || $device eq "0x43a0" || $device eq "0x43b1" ) ) { - push @modulePackages, "\${config.boot.kernelPackages.broadcom_sta}"; + push @modulePackages, "config.boot.kernelPackages.broadcom_sta"; push @kernelModules, "wl"; } @@ -349,7 +349,7 @@ foreach my $fs (read_file("/proc/self/mountinfo")) { fileSystems.\"$mountPoint\" = { device = \"$base$path\"; fsType = \"none\"; - options = \"bind\"; + options = \[ \"bind\" \]; }; EOF @@ -409,7 +409,7 @@ EOF if (scalar @extraOptions > 0) { $fileSystems .= <<EOF; - options = \"${\join ",", uniq(@extraOptions)}\"; + options = \[ ${\join " ", map { "\"" . $_ . "\"" } uniq(@extraOptions)} \]; EOF } @@ -422,13 +422,20 @@ EOF # Generate the hardware configuration file. -sub toNixExpr { +sub toNixStringList { my $res = ""; foreach my $s (@_) { $res .= " \"$s\""; } return $res; } +sub toNixList { + my $res = ""; + foreach my $s (@_) { + $res .= " $s"; + } + return $res; +} sub multiLineList { my $indent = shift; @@ -444,9 +451,9 @@ sub multiLineList { return $res; } -my $initrdAvailableKernelModules = toNixExpr(uniq @initrdAvailableKernelModules); -my $kernelModules = toNixExpr(uniq @kernelModules); -my $modulePackages = toNixExpr(uniq @modulePackages); +my $initrdAvailableKernelModules = toNixStringList(uniq @initrdAvailableKernelModules); +my $kernelModules = toNixStringList(uniq @kernelModules); +my $modulePackages = toNixList(uniq @modulePackages); my $fsAndSwap = ""; if (!$noFilesystems) { diff --git a/nixos/modules/installer/tools/nixos-install.sh b/nixos/modules/installer/tools/nixos-install.sh index 4e10615f902f..c23d7e5b509d 100644 --- a/nixos/modules/installer/tools/nixos-install.sh +++ b/nixos/modules/installer/tools/nixos-install.sh @@ -73,11 +73,6 @@ if ! test -e "$mountPoint"; then exit 1 fi -if ! grep -F -q " $mountPoint " /proc/mounts; then - echo "$mountPoint doesn't appear to be a mount point" - exit 1 -fi - # Mount some stuff in the target root directory. mkdir -m 0755 -p $mountPoint/dev $mountPoint/proc $mountPoint/sys $mountPoint/etc $mountPoint/run $mountPoint/home diff --git a/nixos/modules/installer/tools/nixos-rebuild.sh b/nixos/modules/installer/tools/nixos-rebuild.sh index 6792690b4c3b..e0e79f63fab7 100644 --- a/nixos/modules/installer/tools/nixos-rebuild.sh +++ b/nixos/modules/installer/tools/nixos-rebuild.sh @@ -19,6 +19,8 @@ rollback= upgrade= repair= profile=/nix/var/nix/profiles/system +buildHost= +targetHost= while [ "$#" -gt 0 ]; do i="$1"; shift 1 @@ -73,6 +75,14 @@ while [ "$#" -gt 0 ]; do fi shift 1 ;; + --build-host|h) + buildHost="$1" + shift 1 + ;; + --target-host|t) + targetHost="$1" + shift 1 + ;; *) echo "$0: unknown option \`$i'" exit 1 @@ -80,6 +90,91 @@ while [ "$#" -gt 0 ]; do esac done + +if [ -z "$buildHost" -a -n "$targetHost" ]; then + buildHost="$targetHost" +fi +if [ "$targetHost" = localhost ]; then + targetHost= +fi +if [ "$buildHost" = localhost ]; then + buildHost= +fi + +buildHostCmd() { + if [ -z "$buildHost" ]; then + "$@" + elif [ -n "$remoteNix" ]; then + ssh $SSHOPTS "$buildHost" PATH="$remoteNix:$PATH" "$@" + else + ssh $SSHOPTS "$buildHost" "$@" + fi +} + +targetHostCmd() { + if [ -z "$targetHost" ]; then + "$@" + else + ssh $SSHOPTS "$targetHost" "$@" + fi +} + +copyToTarget() { + if ! [ "$targetHost" = "$buildHost" ]; then + if [ -z "$targetHost" ]; then + NIX_SSHOPTS=$SSH_OPTS nix-copy-closure --from "$buildHost" "$1" + elif [ -z "$buildHost" ]; then + NIX_SSHOPTS=$SSH_OPTS nix-copy-closure --to "$targetHost" "$1" + else + buildHostCmd nix-copy-closure --to "$targetHost" "$1" + fi + fi +} + +nixBuild() { + if [ -z "$buildHost" ]; then + nix-build "$@" + else + local instArgs=() + local buildArgs=() + + while [ "$#" -gt 0 ]; do + local i="$1"; shift 1 + case "$i" in + -o) + local out="$1"; shift 1 + buildArgs+=("--add-root" "$out" "--indirect") + ;; + -A) + local j="$1"; shift 1 + instArgs+=("$i" "$j") + ;; + -I) # We don't want this in buildArgs + shift 1 + ;; + --no-out-link) # We don't want this in buildArgs + ;; + "<"*) # nix paths + instArgs+=("$i") + ;; + *) + buildArgs+=("$i") + ;; + esac + done + + local drv="$(nix-instantiate "${instArgs[@]}" "${extraBuildFlags[@]}")" + if [ -a "$drv" ]; then + NIX_SSHOPTS=$SSH_OPTS nix-copy-closure --to "$buildHost" "$drv" + buildHostCmd nix-store -r "$drv" "${buildArgs[@]}" + else + echo "nix-instantiate failed" + exit 1 + fi + fi +} + + if [ -z "$action" ]; then showSyntax; fi # Only run shell scripts from the Nixpkgs tree if the action is @@ -128,7 +223,16 @@ fi tmpDir=$(mktemp -t -d nixos-rebuild.XXXXXX) -trap 'rm -rf "$tmpDir"' EXIT +SSHOPTS="$NIX_SSHOPTS -o ControlMaster=auto -o ControlPath=$tmpDir/ssh-%n -o ControlPersist=60" + +cleanup() { + for ctrl in "$tmpDir"/ssh-*; do + ssh -o ControlPath="$ctrl" -O exit dummyhost 2>/dev/null || true + done + rm -rf "$tmpDir" +} +trap cleanup EXIT + # If the Nix daemon is running, then use it. This allows us to use @@ -150,30 +254,56 @@ if [ -n "$rollback" -o "$action" = dry-build ]; then buildNix= fi +prebuiltNix() { + machine="$1" + if [ "$machine" = x86_64 ]; then + return /nix/store/xryr9g56h8yjddp89d6dw12anyb4ch7c-nix-1.10 + elif [[ "$machine" =~ i.86 ]]; then + return /nix/store/2w92k5wlpspf0q2k9mnf2z42prx3bwmv-nix-1.10 + else + echo "$0: unsupported platform" + exit 1 + fi +} + +remotePATH= + if [ -n "$buildNix" ]; then echo "building Nix..." >&2 - if ! nix-build '<nixpkgs/nixos>' -A config.nix.package -o $tmpDir/nix "${extraBuildFlags[@]}" > /dev/null; then - if ! nix-build '<nixpkgs/nixos>' -A nixFallback -o $tmpDir/nix "${extraBuildFlags[@]}" > /dev/null; then - if ! nix-build '<nixpkgs>' -A nix -o $tmpDir/nix "${extraBuildFlags[@]}" > /dev/null; then - machine="$(uname -m)" - if [ "$machine" = x86_64 ]; then - nixStorePath=/nix/store/xryr9g56h8yjddp89d6dw12anyb4ch7c-nix-1.10 - elif [[ "$machine" =~ i.86 ]]; then - nixStorePath=/nix/store/2w92k5wlpspf0q2k9mnf2z42prx3bwmv-nix-1.10 - else - echo "$0: unsupported platform" - exit 1 - fi + nixDrv= + if ! nixDrv="$(nix-instantiate '<nixpkgs/nixos>' --add-root $tmpDir/nix.drv --indirect -A config.nix.package "${extraBuildFlags[@]}")"; then + if ! nixDrv="$(nix-instantiate '<nixpkgs/nixos>' --add-root $tmpDir/nix.drv --indirect -A nixFallback "${extraBuildFlags[@]}")"; then + if ! nixDrv="$(nix-instantiate '<nixpkgs>' --add-root $tmpDir/nix.drv --indirect -A nix "${extraBuildFlags[@]}")"; then + nixStorePath="$(prebuiltNix "$(uname -m)")" if ! nix-store -r $nixStorePath --add-root $tmpDir/nix --indirect \ --option extra-binary-caches https://cache.nixos.org/; then echo "warning: don't know how to get latest Nix" >&2 fi # Older version of nix-store -r don't support --add-root. [ -e $tmpDir/nix ] || ln -sf $nixStorePath $tmpDir/nix + if [ -n "$buildHost" ]; then + remoteNixStorePath="$(prebuiltNix "$(buildHostCmd uname -m)")" + remoteNix="$remoteNixStorePath/bin" + if ! buildHostCmd nix-store -r $remoteNixStorePath \ + --option extra-binary-caches https://cache.nixos.org/ >/dev/null; then + remoteNix= + echo "warning: don't know how to get latest Nix" >&2 + fi + fi fi fi fi - PATH=$tmpDir/nix/bin:$PATH + if [ -a "$nixDrv" ]; then + nix-store -r "$nixDrv"'!'"out" --add-root $tmpDir/nix --indirect >/dev/null + if [ -n "$buildHost" ]; then + nix-copy-closure --to "$buildHost" "$nixDrv" + # The nix build produces multiple outputs, we add them all to the remote path + for p in $(buildHostCmd nix-store -r "$(readlink "$nixDrv")" "${buildArgs[@]}"); do + remoteNix="$remoteNix${remoteNix:+:}$p/bin" + done + fi + fi + PATH="$tmpDir/nix/bin:$PATH" fi @@ -200,31 +330,35 @@ fi if [ -z "$rollback" ]; then echo "building the system configuration..." >&2 if [ "$action" = switch -o "$action" = boot ]; then - nix-env "${extraBuildFlags[@]}" -p "$profile" -f '<nixpkgs/nixos>' --set -A system - pathToConfig="$profile" + pathToConfig="$(nixBuild '<nixpkgs/nixos>' --no-out-link -A system "${extraBuildFlags[@]}")" + copyToTarget "$pathToConfig" + targetHostCmd nix-env -p "$profile" --set "$pathToConfig" elif [ "$action" = test -o "$action" = build -o "$action" = dry-build -o "$action" = dry-activate ]; then - nix-build '<nixpkgs/nixos>' -A system -k "${extraBuildFlags[@]}" > /dev/null - pathToConfig=./result + pathToConfig="$(nixBuild '<nixpkgs/nixos>' -A system -k "${extraBuildFlags[@]}")" elif [ "$action" = build-vm ]; then - nix-build '<nixpkgs/nixos>' -A vm -k "${extraBuildFlags[@]}" > /dev/null - pathToConfig=./result + pathToConfig="$(nixBuild '<nixpkgs/nixos>' -A vm -k "${extraBuildFlags[@]}")" elif [ "$action" = build-vm-with-bootloader ]; then - nix-build '<nixpkgs/nixos>' -A vmWithBootLoader -k "${extraBuildFlags[@]}" > /dev/null - pathToConfig=./result + pathToConfig="$(nixBuild '<nixpkgs/nixos>' -A vmWithBootLoader -k "${extraBuildFlags[@]}")" else showSyntax fi + # Copy build to target host if we haven't already done it + if ! [ "$action" = switch -o "$action" = boot ]; then + copyToTarget "$pathToConfig" + fi else # [ -n "$rollback" ] if [ "$action" = switch -o "$action" = boot ]; then - nix-env --rollback -p "$profile" + targetHostCmd nix-env --rollback -p "$profile" pathToConfig="$profile" elif [ "$action" = test -o "$action" = build ]; then systemNumber=$( - nix-env -p "$profile" --list-generations | + targetHostCmd nix-env -p "$profile" --list-generations | sed -n '/current/ {g; p;}; s/ *\([0-9]*\).*/\1/; h' ) - ln -sT "$profile"-${systemNumber}-link ./result - pathToConfig=./result + pathToConfig="$profile"-${systemNumber}-link + if [ -z "$targetHost" ]; then + ln -sT "$pathToConfig" ./result + fi else showSyntax fi @@ -234,7 +368,7 @@ fi # If we're not just building, then make the new configuration the boot # default and/or activate it now. if [ "$action" = switch -o "$action" = boot -o "$action" = test -o "$action" = dry-activate ]; then - if ! $pathToConfig/bin/switch-to-configuration "$action"; then + if ! targetHostCmd $pathToConfig/bin/switch-to-configuration "$action"; then echo "warning: error(s) occurred while switching to the new configuration" >&2 exit 1 fi diff --git a/nixos/modules/misc/ids.nix b/nixos/modules/misc/ids.nix index 064b4cbc4b33..0ab2b8a76fc5 100644 --- a/nixos/modules/misc/ids.nix +++ b/nixos/modules/misc/ids.nix @@ -248,6 +248,12 @@ matrix-synapse = 224; rspamd = 225; rmilter = 226; + cfdyndns = 227; + gammu-smsd = 228; + pdnsd = 229; + octoprint = 230; + avahi-autoipd = 231; + nntp-proxy = 232; # When adding a uid, make sure it doesn't match an existing gid. And don't use uids above 399! @@ -473,6 +479,9 @@ matrix-synapse = 224; rspamd = 225; rmilter = 226; + cfdyndns = 227; + pdnsd = 229; + octoprint = 230; # When adding a gid, make sure it doesn't match an existing # uid. Users and groups with the same name should have equal diff --git a/nixos/modules/misc/locate.nix b/nixos/modules/misc/locate.nix index 4f9c8d4e5ba1..5b560cedc623 100644 --- a/nixos/modules/misc/locate.nix +++ b/nixos/modules/misc/locate.nix @@ -1,76 +1,76 @@ -{ config, lib, pkgs, ... }: +{ config, options, lib, pkgs, ... }: with lib; let cfg = config.services.locate; in { + options.services.locate = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + If enabled, NixOS will periodically update the database of + files used by the <command>locate</command> command. + ''; + }; - ###### interface - - options = { - - services.locate = { - - enable = mkOption { - type = types.bool; - default = false; - description = '' - If enabled, NixOS will periodically update the database of - files used by the <command>locate</command> command. - ''; - }; - - period = mkOption { - type = types.str; - default = "15 02 * * *"; - description = '' - This option defines (in the format used by cron) when the - locate database is updated. - The default is to update at 02:15 at night every day. - ''; - }; - - extraFlags = mkOption { - type = types.listOf types.str; - default = [ ]; - description = '' - Extra flags to pass to <command>updatedb</command>. - ''; - }; + interval = mkOption { + type = types.str; + default = "02:15"; + example = "hourly"; + description = '' + Update the locate database at this interval. Updates by + default at 2:15 AM every day. + + The format is described in + <citerefentry><refentrytitle>systemd.time</refentrytitle> + <manvolnum>7</manvolnum></citerefentry>. + ''; + }; - output = mkOption { - type = types.path; - default = "/var/cache/locatedb"; - description = '' - The database file to build. - ''; - }; + # This is no longer supported, but we keep it to give a better warning below + period = mkOption { visible = false; }; - localuser = mkOption { - type = types.str; - default = "nobody"; - description = '' - The user to search non-network directories as, using - <command>su</command>. - ''; - }; + extraFlags = mkOption { + type = types.listOf types.str; + default = [ ]; + description = '' + Extra flags to pass to <command>updatedb</command>. + ''; + }; - includeStore = mkOption { - type = types.bool; - default = false; - description = '' - Whether to include <filename>/nix/store</filename> in the locate database. - ''; - }; + output = mkOption { + type = types.path; + default = "/var/cache/locatedb"; + description = '' + The database file to build. + ''; + }; + localuser = mkOption { + type = types.str; + default = "nobody"; + description = '' + The user to search non-network directories as, using + <command>su</command>. + ''; }; + includeStore = mkOption { + type = types.bool; + default = false; + description = '' + Whether to include <filename>/nix/store</filename> in the locate database. + ''; + }; }; - ###### implementation - config = { + warnings = + let opt = options.services.locate.period; in + optional opt.isDefined "The ‘services.locate.period’ option in ${showFiles opt.files} has been removed; please replace it with ‘services.locate.interval’, using the systemd.time(7) calendar event format."; + systemd.services.update-locatedb = { description = "Update Locate Database"; path = [ pkgs.su ]; @@ -84,11 +84,18 @@ in { ''; serviceConfig.Nice = 19; serviceConfig.IOSchedulingClass = "idle"; + serviceConfig.PrivateTmp = "yes"; + serviceConfig.PrivateNetwork = "yes"; + serviceConfig.NoNewPrivileges = "yes"; + serviceConfig.ReadOnlyDirectories = "/"; + serviceConfig.ReadWriteDirectories = cfg.output; }; - services.cron.systemCronJobs = optional config.services.locate.enable - "${config.services.locate.period} root ${config.systemd.package}/bin/systemctl start update-locatedb.service"; - + systemd.timers.update-locatedb = mkIf cfg.enable + { description = "Update timer for locate database"; + partOf = [ "update-locatedb.service" ]; + wantedBy = [ "timers.target" ]; + timerConfig.OnCalendar = cfg.interval; + }; }; - } diff --git a/nixos/modules/misc/nixos.nix b/nixos/modules/misc/nixos.nix deleted file mode 100644 index 84365b640a48..000000000000 --- a/nixos/modules/misc/nixos.nix +++ /dev/null @@ -1,82 +0,0 @@ -{ config, options, lib, ... }: - -# This modules is used to inject a different NixOS version as well as its -# argument such that one can pin a specific version with the versionning -# system of the configuration. -let - nixosReentry = import config.nixos.path { - inherit (config.nixos) configuration extraModules; - inherit (config.nixpkgs) system; - reEnter = true; - }; -in - -with lib; - -{ - options = { - nixos.path = mkOption { - default = null; - example = literalExample "./nixpkgs-15.09/nixos"; - type = types.nullOr types.path; - description = '' - This option give the ability to evaluate the current set of modules - with a different version of NixOS. This option can be used version - the version of NixOS with the configuration without relying on the - <literal>NIX_PATH</literal> environment variable. - ''; - }; - - nixos.system = mkOption { - example = "i686-linux"; - type = types.uniq types.str; - description = '' - Name of the system used to compile NixOS. - ''; - }; - - nixos.extraModules = mkOption { - default = []; - example = literalExample "[ ./sshd-config.nix ]"; - type = types.listOf (types.either (types.submodule ({...}:{options={};})) types.path); - description = '' - Define additional modules which would be loaded to evaluate the - configuration. - ''; - }; - - nixos.configuration = mkOption { - type = types.unspecified; - internal = true; - description = '' - Option used by <filename>nixos/default.nix</filename> to re-inject - the same configuration module as the one used for the current - execution. - ''; - }; - - nixos.reflect = mkOption { - default = { inherit config options; }; - type = types.unspecified; - internal = true; - description = '' - Provides <literal>config</literal> and <literal>options</literal> - computed by the module system and given as argument to all - modules. These are used for introspection of options and - configuration by tools such as <literal>nixos-option</literal>. - ''; - }; - }; - - config = mkMerge [ - (mkIf (config.nixos.path != null) (mkForce { - system.build.toplevel = nixosReentry.system; - system.build.vm = nixosReentry.vm; - nixos.reflect = { inherit (nixosReentry) config options; }; - })) - - { meta.maintainers = singleton lib.maintainers.pierron; - meta.doc = ./nixos.xml; - } - ]; -} diff --git a/nixos/modules/misc/nixos.xml b/nixos/modules/misc/nixos.xml deleted file mode 100644 index 064bdd80b3c9..000000000000 --- a/nixos/modules/misc/nixos.xml +++ /dev/null @@ -1,84 +0,0 @@ -<chapter xmlns="http://docbook.org/ns/docbook" - xmlns:xlink="http://www.w3.org/1999/xlink" - xmlns:xi="http://www.w3.org/2001/XInclude" - version="5.0" - xml:id="module-misc-nixos"> - -<title>NixOS Reentry</title> - -<!-- FIXME: render nicely --> - -<!-- FIXME: source can be added automatically --> -<para><emphasis>Source:</emphasis> <filename>modules/misc/nixos.nix</filename></para> - -<!-- FIXME: more stuff, like maintainer? --> - -<para>NixOS reentry can be used for both pinning the evaluation to a -specific version of NixOS, and to dynamically add additional modules into -the Module evaluation.</para> - -<section><title>NixOS Version Pinning</title> - -<para>To pin a specific version of NixOS, you need a version that you can -either clone localy, or that you can fetch remotely.</para> - -<para>If you already have a cloned version of NixOS in the directory -<filename>/etc/nixos/nixpkgs-16-03</filename>, then you can specify the -<option>nixos.path</option> with either the path or the relative path of -your NixOS clone. For example, you can add the following to your -<filename>/etc/nixos/configuration.nix</filename> file: - -<programlisting> -nixos.path = ./nixpkgs-16-03/nixos; -</programlisting> -</para> - -<para>Another option is to fetch a specific version of NixOS, with either -the <literal>fetchTarball</literal> builtin, or the -<literal>pkgs.fetchFromGitHub</literal> function and use the result as an -input. - -<programlisting> -nixos.path = "${builtins.fetchTarball https://github.com/NixOS/nixpkgs/archive/1f27976e03c15183191d1b4aa1a40d1f14666cd2.tar.gz}/nixos"; -</programlisting> -</para> - -</section> - - -<section><title>Adding Module Dynamically</title> - -<para>To add additional module, the recommended way is to use statically -known modules in the list of imported arguments as described in <xref -linkend="sec-modularity" />. Unfortunately, this recommended method has -limitation, such that the list of imported files cannot be selected based on -the content of the configuration. - -Fortunately, NixOS reentry system can be used as an alternative to register -new imported modules based on the content of the configuration. To do so, -one should define both <option>nixos.path</option> and -<option>nixos.extraModules</option> options. - -<programlisting> -nixos.path = <nixos>; -nixos.extraModules = - if config.networking.hostName == "server" then - [ ./server.nix ] else [ ./client.nix ]; -</programlisting> - -Also note, that the above can be reimplemented in a different way which is -not as expensive, by using <literal>mkIf</literal> at the top each -configuration if both modules are present on the file system (see <xref -linkend="sec-option-definitions" />) and by always inmporting both -modules.</para> - -</section> - -<section><title>Options</title> - -<para>FIXME: auto-generated list of module options.</para> - -</section> - - -</chapter> diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 2ff61877c23d..0105cc3cdf2c 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -7,7 +7,6 @@ ./config/fonts/fonts.nix ./config/fonts/ghostscript.nix ./config/gnu.nix - ./config/gtk-exe-env.nix ./config/i18n.nix ./config/krb5.nix ./config/ldap.nix @@ -16,7 +15,6 @@ ./config/nsswitch.nix ./config/power-management.nix ./config/pulseaudio.nix - ./config/qt-plugin-env.nix ./config/shells-environment.nix ./config/swap.nix ./config/sysctl.nix @@ -42,6 +40,12 @@ ./hardware/video/bumblebee.nix ./hardware/video/nvidia.nix ./hardware/video/ati.nix + ./hardware/video/webcam/facetimehd.nix + ./i18n/inputMethod/default.nix + ./i18n/inputMethod/fcitx.nix + ./i18n/inputMethod/ibus.nix + ./i18n/inputMethod/nabi.nix + ./i18n/inputMethod/uim.nix ./installer/tools/auto-upgrade.nix ./installer/tools/nixos-checkout.nix ./installer/tools/tools.nix @@ -52,7 +56,6 @@ ./misc/lib.nix ./misc/locate.nix ./misc/meta.nix - ./misc/nixos.nix ./misc/nixpkgs.nix ./misc/passthru.nix ./misc/version.nix @@ -65,7 +68,6 @@ ./programs/environment.nix ./programs/freetds.nix ./programs/fish.nix - ./programs/ibus.nix ./programs/kbdlight.nix ./programs/light.nix ./programs/man.nix @@ -75,7 +77,6 @@ ./programs/shell.nix ./programs/ssh.nix ./programs/ssmtp.nix - ./programs/uim.nix ./programs/venus.nix ./programs/wvdial.nix ./programs/xfs_quota.nix @@ -88,6 +89,7 @@ ./security/ca.nix ./security/duosec.nix ./security/grsecurity.nix + ./security/oath.nix ./security/pam.nix ./security/pam_usb.nix ./security/pam_mount.nix @@ -200,6 +202,7 @@ ./services/misc/bepasty.nix ./services/misc/canto-daemon.nix ./services/misc/calibre-server.nix + ./services/misc/cfdyndns.nix ./services/misc/cpuminer-cryptonight.nix ./services/misc/cgminer.nix ./services/misc/confd.nix @@ -210,6 +213,7 @@ ./services/misc/etcd.nix ./services/misc/felix.nix ./services/misc/folding-at-home.nix + ./services/misc/gammu-smsd.nix #./services/misc/gitit.nix ./services/misc/gitlab.nix ./services/misc/gitolite.nix @@ -226,6 +230,7 @@ ./services/misc/nix-gc.nix ./services/misc/nixos-manual.nix ./services/misc/nix-ssh-serve.nix + ./services/misc/octoprint.nix ./services/misc/parsoid.nix ./services/misc/phd.nix ./services/misc/plex.nix @@ -235,6 +240,7 @@ ./services/misc/ripple-data-api.nix ./services/misc/rogue.nix ./services/misc/siproxd.nix + ./services/misc/spice-vdagentd.nix ./services/misc/subsonic.nix ./services/misc/sundtek.nix ./services/misc/svnserve.nix @@ -249,6 +255,7 @@ ./services/monitoring/dd-agent.nix ./services/monitoring/grafana.nix ./services/monitoring/graphite.nix + ./services/monitoring/hdaps.nix ./services/monitoring/heapster.nix ./services/monitoring/longview.nix ./services/monitoring/monit.nix @@ -267,6 +274,7 @@ ./services/monitoring/zabbix-agent.nix ./services/monitoring/zabbix-server.nix ./services/network-filesystems/drbd.nix + ./services/network-filesystems/netatalk.nix ./services/network-filesystems/nfsd.nix ./services/network-filesystems/openafs-client/default.nix ./services/network-filesystems/rsyncd.nix @@ -315,11 +323,11 @@ ./services/networking/hostapd.nix ./services/networking/i2pd.nix ./services/networking/i2p.nix - ./services/networking/ifplugd.nix ./services/networking/iodined.nix ./services/networking/ircd-hybrid/default.nix ./services/networking/kippo.nix ./services/networking/lambdabot.nix + ./services/networking/libreswan.nix ./services/networking/mailpile.nix ./services/networking/minidlna.nix ./services/networking/miniupnpd.nix @@ -330,6 +338,7 @@ ./services/networking/networkmanager.nix ./services/networking/ngircd.nix ./services/networking/nix-serve.nix + ./services/networking/nntp-proxy.nix ./services/networking/nsd.nix ./services/networking/ntopng.nix ./services/networking/ntpd.nix @@ -339,6 +348,7 @@ ./services/networking/openntpd.nix ./services/networking/openvpn.nix ./services/networking/ostinato.nix + ./services/networking/pdnsd.nix ./services/networking/polipo.nix ./services/networking/prayer.nix ./services/networking/privoxy.nix @@ -395,6 +405,7 @@ ./services/security/fprintd.nix ./services/security/fprot.nix ./services/security/frandom.nix + ./services/security/haka.nix ./services/security/haveged.nix ./services/security/hologram.nix ./services/security/munge.nix @@ -437,6 +448,7 @@ ./services/x11/display-managers/lightdm.nix ./services/x11/display-managers/sddm.nix ./services/x11/display-managers/slim.nix + ./services/x11/hardware/libinput.nix ./services/x11/hardware/multitouch.nix ./services/x11/hardware/synaptics.nix ./services/x11/hardware/wacom.nix @@ -460,10 +472,10 @@ ./system/boot/coredump.nix ./system/boot/emergency-mode.nix ./system/boot/initrd-network.nix + ./system/boot/initrd-ssh.nix ./system/boot/kernel.nix ./system/boot/kexec.nix ./system/boot/loader/efi.nix - ./system/boot/loader/loader.nix ./system/boot/loader/generations-dir/generations-dir.nix ./system/boot/loader/generic-extlinux-compatible ./system/boot/loader/grub/grub.nix @@ -471,15 +483,16 @@ ./system/boot/loader/grub/memtest.nix ./system/boot/loader/gummiboot/gummiboot.nix ./system/boot/loader/init-script/init-script.nix + ./system/boot/loader/loader.nix ./system/boot/loader/raspberrypi/raspberrypi.nix ./system/boot/luksroot.nix ./system/boot/modprobe.nix + ./system/boot/networkd.nix + ./system/boot/resolved.nix ./system/boot/shutdown.nix ./system/boot/stage-1.nix ./system/boot/stage-2.nix ./system/boot/systemd.nix - ./system/boot/networkd.nix - ./system/boot/resolved.nix ./system/boot/timesyncd.nix ./system/boot/tmp.nix ./system/etc/etc.nix diff --git a/nixos/modules/profiles/base.nix b/nixos/modules/profiles/base.nix index 20a1f7f1ed8c..66b5765a895c 100644 --- a/nixos/modules/profiles/base.nix +++ b/nixos/modules/profiles/base.nix @@ -17,6 +17,7 @@ pkgs.ddrescue pkgs.ccrypt pkgs.cryptsetup # needed for dm-crypt volumes + pkgs.which # 88K size # Some networking tools. pkgs.fuse diff --git a/nixos/modules/profiles/qemu-guest.nix b/nixos/modules/profiles/qemu-guest.nix index 759fdb7f8e5f..987eb051b98c 100644 --- a/nixos/modules/profiles/qemu-guest.nix +++ b/nixos/modules/profiles/qemu-guest.nix @@ -14,4 +14,6 @@ # to the *boot time* of the host). hwclock -s ''; + + security.rngd.enable = false; } diff --git a/nixos/modules/programs/bash/bash.nix b/nixos/modules/programs/bash/bash.nix index 1c3c07a1c210..e4e264ec0036 100644 --- a/nixos/modules/programs/bash/bash.nix +++ b/nixos/modules/programs/bash/bash.nix @@ -56,7 +56,7 @@ in */ shellAliases = mkOption { - default = config.environment.shellAliases // { which = "type -P"; }; + default = config.environment.shellAliases; description = '' Set of aliases for bash shell. See <option>environment.shellAliases</option> for an option format description. diff --git a/nixos/modules/programs/freetds.nix b/nixos/modules/programs/freetds.nix index 398fd104363b..e0860a242b74 100644 --- a/nixos/modules/programs/freetds.nix +++ b/nixos/modules/programs/freetds.nix @@ -17,14 +17,14 @@ in environment.freetds = mkOption { type = types.attrsOf types.str; default = {}; - example = { - MYDATABASE = - '' - host = 10.0.2.100 - port = 1433 - tds version = 7.2 - ''; - }; + example = literalExample '' + { MYDATABASE = ''' + host = 10.0.2.100 + port = 1433 + tds version = 7.2 + '''; + } + ''; description = '' Configure freetds database entries. Each attribute denotes diff --git a/nixos/modules/programs/ibus.nix b/nixos/modules/programs/ibus.nix deleted file mode 100644 index a42753a292b2..000000000000 --- a/nixos/modules/programs/ibus.nix +++ /dev/null @@ -1,51 +0,0 @@ -{ config, pkgs, lib, ... }: - -with lib; - -let - cfg = config.programs.ibus; -in -{ - options = { - - programs.ibus = { - enable = mkOption { - type = types.bool; - default = false; - example = true; - description = "Enable IBus input method"; - }; - plugins = mkOption { - type = lib.types.listOf lib.types.path; - default = []; - description = '' - IBus plugin packages - ''; - }; - }; - - }; - - config = mkIf cfg.enable { - environment.systemPackages = [ pkgs.ibus pkgs.gnome3.dconf ]; - - gtkPlugins = [ pkgs.ibus ]; - qtPlugins = [ pkgs.ibus-qt ]; - - environment.variables = - let - env = pkgs.buildEnv { - name = "ibus-env"; - paths = [ pkgs.ibus ] ++ cfg.plugins; - }; - in { - GTK_IM_MODULE = "ibus"; - QT_IM_MODULE = "ibus"; - XMODIFIERS = "@im=ibus"; - - IBUS_COMPONENT_PATH = "${env}/share/ibus/component"; - }; - - services.xserver.displayManager.sessionCommands = "${pkgs.ibus}/bin/ibus-daemon --daemonize --xim --cache=none"; - }; -} diff --git a/nixos/modules/programs/ssh.nix b/nixos/modules/programs/ssh.nix index 260888be485f..1ad45f468030 100644 --- a/nixos/modules/programs/ssh.nix +++ b/nixos/modules/programs/ssh.nix @@ -36,6 +36,7 @@ in askPassword = mkOption { type = types.str; + default = "${pkgs.x11_ssh_askpass}/libexec/x11-ssh-askpass"; description = ''Program used by SSH to ask for passwords.''; }; @@ -186,6 +187,9 @@ in ForwardX11 ${if cfg.forwardX11 then "yes" else "no"} + # Allow DSA keys for now. (These were deprecated in OpenSSH 7.0.) + PubkeyAcceptedKeyTypes +ssh-dss + ${cfg.extraConfig} ''; @@ -221,12 +225,7 @@ in fi ''; - environment.interactiveShellInit = optionalString config.services.xserver.enable - '' - export SSH_ASKPASS=${askPassword} - ''; - - programs.ssh.askPassword = mkDefault "${pkgs.x11_ssh_askpass}/libexec/x11-ssh-askpass"; + environment.variables.SSH_ASKPASS = optionalString config.services.xserver.enable askPassword; }; } diff --git a/nixos/modules/programs/uim.nix b/nixos/modules/programs/uim.nix deleted file mode 100644 index 4bf2f9a17571..000000000000 --- a/nixos/modules/programs/uim.nix +++ /dev/null @@ -1,31 +0,0 @@ -{ config, pkgs, lib, ... }: - -with lib; - -let - cfg = config.uim; -in -{ - options = { - - uim = { - enable = mkOption { - type = types.bool; - default = false; - example = true; - description = "Enable UIM input method"; - }; - }; - - }; - - config = mkIf cfg.enable { - environment.systemPackages = [ pkgs.uim ]; - gtkPlugins = [ pkgs.uim ]; - qtPlugins = [ pkgs.uim ]; - environment.variables.GTK_IM_MODULE = "uim"; - environment.variables.QT_IM_MODULE = "uim"; - environment.variables.XMODIFIERS = "@im=uim"; - services.xserver.displayManager.sessionCommands = "uim-xim &"; - }; -} diff --git a/nixos/modules/programs/venus.nix b/nixos/modules/programs/venus.nix index 8f85b602fe2c..731ebed14c7b 100644 --- a/nixos/modules/programs/venus.nix +++ b/nixos/modules/programs/venus.nix @@ -99,6 +99,7 @@ in }; outputTheme = mkOption { + default = "${pkgs.venus}/themes/classic_fancy"; type = types.path; description = '' Directory containing a config.ini file which is merged with this one. @@ -165,11 +166,8 @@ in script = "exec venus-planet ${configFile}"; serviceConfig.User = "${cfg.user}"; serviceConfig.Group = "${cfg.group}"; - environment.SSL_CERT_FILE = "/etc/ssl/certs/ca-bundle.crt"; startAt = cfg.dates; }; - services.venus.outputTheme = mkDefault "${pkgs.venus}/themes/classic_fancy"; - }; } diff --git a/nixos/modules/programs/xfs_quota.nix b/nixos/modules/programs/xfs_quota.nix index 90b6304fa999..648fd9a8a94f 100644 --- a/nixos/modules/programs/xfs_quota.nix +++ b/nixos/modules/programs/xfs_quota.nix @@ -89,8 +89,8 @@ in nameValuePair "xfs_quota-${name}" { description = "Setup xfs_quota for project ${name}"; script = '' - ${pkgs.xfsprogs}/bin/xfs_quota -x -c 'project -s ${name}' ${opts.fileSystem} - ${pkgs.xfsprogs}/bin/xfs_quota -x -c 'limit -p ${limitOptions opts} ${name}' ${opts.fileSystem} + ${pkgs.xfsprogs.bin}/bin/xfs_quota -x -c 'project -s ${name}' ${opts.fileSystem} + ${pkgs.xfsprogs.bin}/bin/xfs_quota -x -c 'limit -p ${limitOptions opts} ${name}' ${opts.fileSystem} ''; wantedBy = [ "multi-user.target" ]; diff --git a/nixos/modules/rename.nix b/nixos/modules/rename.nix index 010d44c40d19..85435884b199 100644 --- a/nixos/modules/rename.nix +++ b/nixos/modules/rename.nix @@ -28,6 +28,9 @@ with lib; (mkRenamedOptionModule [ "services" "subsonic" "host" ] [ "services" "subsonic" "listenAddress" ]) (mkRenamedOptionModule [ "jobs" ] [ "systemd" "services" ]) + (mkRenamedOptionModule [ "services" "gitlab" "stateDir" ] [ "services" "gitlab" "statePath" ]) + (mkRemovedOptionModule [ "services" "gitlab" "satelliteDir" ]) + # Old Grub-related options. (mkRenamedOptionModule [ "boot" "initrd" "extraKernelModules" ] [ "boot" "initrd" "kernelModules" ]) (mkRenamedOptionModule [ "boot" "extraKernelParams" ] [ "boot" "kernelParams" ]) @@ -59,6 +62,9 @@ with lib; # Tarsnap (mkRenamedOptionModule [ "services" "tarsnap" "config" ] [ "services" "tarsnap" "archives" ]) + # ibus + (mkRenamedOptionModule [ "programs" "ibus" "plugins" ] [ "i18n" "inputMethod" "ibus" "engines" ]) + # proxy (mkRenamedOptionModule [ "nix" "proxy" ] [ "networking" "proxy" "default" ]) @@ -77,6 +83,7 @@ with lib; (mkRenamedOptionModule [ "services" "xserver" "driSupport32Bit" ] [ "hardware" "opengl" "driSupport32Bit" ]) (mkRenamedOptionModule [ "services" "xserver" "s3tcSupport" ] [ "hardware" "opengl" "s3tcSupport" ]) (mkRenamedOptionModule [ "hardware" "opengl" "videoDrivers" ] [ "services" "xserver" "videoDrivers" ]) + (mkRenamedOptionModule [ "services" "xserver" "vaapiDrivers" ] [ "hardware" "opengl" "extraPackages" ]) (mkRenamedOptionModule [ "services" "mysql55" ] [ "services" "mysql" ]) @@ -99,6 +106,8 @@ with lib; (mkRemovedOptionModule [ "services" "syslog-ng" "listenToJournal" ]) (mkRemovedOptionModule [ "ec2" "metadata" ]) (mkRemovedOptionModule [ "services" "openvpn" "enable" ]) + (mkRemovedOptionModule [ "services" "printing" "cupsFilesConf" ]) + (mkRemovedOptionModule [ "services" "printing" "cupsdConf" ]) ]; } diff --git a/nixos/modules/security/acme.nix b/nixos/modules/security/acme.nix index 15e5b49878f6..c5cd0fb60ee9 100644 --- a/nixos/modules/security/acme.nix +++ b/nixos/modules/security/acme.nix @@ -56,8 +56,8 @@ let plugins = mkOption { type = types.listOf (types.enum [ - "cert.der" "cert.pem" "chain.der" "chain.pem" "external_pem.sh" - "fullchain.der" "fullchain.pem" "key.der" "key.pem" "account_key.json" + "cert.der" "cert.pem" "chain.pem" "external.sh" + "fullchain.pem" "full.pem" "key.der" "key.pem" "account_key.json" ]); default = [ "fullchain.pem" "key.pem" "account_key.json" ]; description = '' diff --git a/nixos/modules/security/apparmor-suid.nix b/nixos/modules/security/apparmor-suid.nix index 3da6a81158d0..d766f6badfc7 100644 --- a/nixos/modules/security/apparmor-suid.nix +++ b/nixos/modules/security/apparmor-suid.nix @@ -29,8 +29,8 @@ with lib; network inet raw, ${pkgs.glibc.out}/lib/*.so mr, - ${pkgs.libcap}/lib/libcap.so* mr, - ${pkgs.attr}/lib/libattr.so* mr, + ${pkgs.libcap.out}/lib/libcap.so* mr, + ${pkgs.attr.out}/lib/libattr.so* mr, ${pkgs.iputils}/bin/ping mixr, /var/setuid-wrappers/ping.real r, diff --git a/nixos/modules/security/audit.nix b/nixos/modules/security/audit.nix index 3aa31e079073..f223f52ec487 100644 --- a/nixos/modules/security/audit.nix +++ b/nixos/modules/security/audit.nix @@ -93,9 +93,11 @@ in { config = mkIf (cfg.enable == "lock" || cfg.enable) { systemd.services.audit = { - description = "pseudo-service representing the kernel audit state"; + description = "Kernel Auditing"; wantedBy = [ "basic.target" ]; + unitConfig.ConditionVirtualization = "!container"; + path = [ pkgs.audit ]; serviceConfig = { diff --git a/nixos/modules/security/ca.nix b/nixos/modules/security/ca.nix index ddfad52d42ed..849530238e7e 100644 --- a/nixos/modules/security/ca.nix +++ b/nixos/modules/security/ca.nix @@ -4,7 +4,7 @@ with lib; let - caBundle = pkgs.runCommand "ca-bundle.crt" + caCertificates = pkgs.runCommand "ca-certificates.crt" { files = config.security.pki.certificateFiles ++ [ (builtins.toFile "extra.crt" (concatStringsSep "\n" config.security.pki.certificates)) ]; @@ -26,7 +26,7 @@ in description = '' A list of files containing trusted root certificates in PEM format. These are concatenated to form - <filename>/etc/ssl/certs/ca-bundle.crt</filename>, which is + <filename>/etc/ssl/certs/ca-certificates.crt</filename>, which is used by many programs that use OpenSSL, such as <command>curl</command> and <command>git</command>. ''; @@ -35,14 +35,17 @@ in security.pki.certificates = mkOption { type = types.listOf types.str; default = []; - example = singleton '' - NixOS.org - ========= - -----BEGIN CERTIFICATE----- - MIIGUDCCBTigAwIBAgIDD8KWMA0GCSqGSIb3DQEBBQUAMIGMMQswCQYDVQQGEwJJ - TDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0 - ... - -----END CERTIFICATE----- + example = literalExample '' + [ ''' + NixOS.org + ========= + -----BEGIN CERTIFICATE----- + MIIGUDCCBTigAwIBAgIDD8KWMA0GCSqGSIb3DQEBBQUAMIGMMQswCQYDVQQGEwJJ + TDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0 + ... + -----END CERTIFICATE----- + ''' + ] ''; description = '' A list of trusted root certificates in PEM format. @@ -56,19 +59,13 @@ in security.pki.certificateFiles = [ "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt" ]; # NixOS canonical location + Debian/Ubuntu/Arch/Gentoo compatibility. - environment.etc."ssl/certs/ca-certificates.crt".source = caBundle; + environment.etc."ssl/certs/ca-certificates.crt".source = caCertificates; # Old NixOS compatibility. - environment.etc."ssl/certs/ca-bundle.crt".source = caBundle; + environment.etc."ssl/certs/ca-bundle.crt".source = caCertificates; # CentOS/Fedora compatibility. - environment.etc."pki/tls/certs/ca-bundle.crt".source = caBundle; - - environment.sessionVariables = - { SSL_CERT_FILE = "/etc/ssl/certs/ca-certificates.crt"; - # FIXME: unneeded - remove eventually. - GIT_SSL_CAINFO = "/etc/ssl/certs/ca-certificates.crt"; - }; + environment.etc."pki/tls/certs/ca-bundle.crt".source = caCertificates; }; diff --git a/nixos/modules/security/oath.nix b/nixos/modules/security/oath.nix new file mode 100644 index 000000000000..20f3e2dd9f83 --- /dev/null +++ b/nixos/modules/security/oath.nix @@ -0,0 +1,50 @@ +# This module provides configuration for the OATH PAM modules. + +{ config, lib, pkgs, ... }: + +with lib; + +{ + options = { + + security.pam.oath = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Enable the OATH (one-time password) PAM module. + ''; + }; + + digits = mkOption { + type = types.enum [ 6 7 8 ]; + default = 6; + description = '' + Specify the length of the one-time password in number of + digits. + ''; + }; + + window = mkOption { + type = types.int; + default = 5; + description = '' + Specify the number of one-time passwords to check in order + to accommodate for situations where the system and the + client are slightly out of sync (iteration for HOTP or time + steps for TOTP). + ''; + }; + + usersFile = mkOption { + type = types.path; + default = "/etc/users.oath"; + description = '' + Set the path to file where the user's credentials are + stored. This file must not be world readable! + ''; + }; + }; + + }; +} diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix index 2ee8a803d2fe..021c561af759 100644 --- a/nixos/modules/security/pam.nix +++ b/nixos/modules/security/pam.nix @@ -75,7 +75,7 @@ let }; oathAuth = mkOption { - default = config.security.pam.enableOATH; + default = config.security.pam.oath.enable; type = types.bool; description = '' If set, the OATH Toolkit will be used. @@ -259,8 +259,8 @@ let "auth sufficient pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} likeauth try_first_pass"} ${optionalString cfg.otpwAuth "auth sufficient ${pkgs.otpw}/lib/security/pam_otpw.so"} - ${optionalString cfg.oathAuth - "auth sufficient ${pkgs.oathToolkit}/lib/security/pam_oath.so window=5 usersfile=/etc/users.oath"} + ${let oath = config.security.pam.oath; in optionalString cfg.oathAuth + "auth sufficient ${pkgs.oathToolkit}/lib/security/pam_oath.so window=${toString oath.window} usersfile=${toString oath.usersFile} digits=${toString oath.digits}"} ${optionalString config.users.ldap.enable "auth sufficient ${pam_ldap}/lib/security/pam_ldap.so use_first_pass"} ${optionalString config.krb5.enable '' @@ -302,8 +302,6 @@ let "session optional ${pam_krb5}/lib/security/pam_krb5.so"} ${optionalString cfg.otpwAuth "session optional ${pkgs.otpw}/lib/security/pam_otpw.so"} - ${optionalString cfg.oathAuth - "session optional ${pkgs.oathToolkit}/lib/security/pam_oath.so window=5 usersfile=/etc/users.oath"} ${optionalString cfg.startSession "session optional ${pkgs.systemd}/lib/security/pam_systemd.so"} ${optionalString cfg.forwardXAuth @@ -405,13 +403,6 @@ in ''; }; - security.pam.enableOATH = mkOption { - default = false; - description = '' - Enable the OATH (one-time password) PAM module. - ''; - }; - security.pam.enableU2F = mkOption { default = false; description = '' @@ -446,7 +437,7 @@ in ++ optional config.users.ldap.enable pam_ldap ++ optionals config.krb5.enable [pam_krb5 pam_ccreds] ++ optionals config.security.pam.enableOTPW [ pkgs.otpw ] - ++ optionals config.security.pam.enableOATH [ pkgs.oathToolkit ] + ++ optionals config.security.pam.oath.enable [ pkgs.oathToolkit ] ++ optionals config.security.pam.enableU2F [ pkgs.pam_u2f ] ++ optionals config.security.pam.enableEcryptfs [ pkgs.ecryptfs ]; diff --git a/nixos/modules/services/amqp/activemq/default.nix b/nixos/modules/services/amqp/activemq/default.nix index 56ff388f8a9e..261f97617664 100644 --- a/nixos/modules/services/amqp/activemq/default.nix +++ b/nixos/modules/services/amqp/activemq/default.nix @@ -32,6 +32,7 @@ in { ''; }; configurationDir = mkOption { + default = "${activemq}/conf"; description = '' The base directory for ActiveMQ's configuration. By default, this directory is searched for a file named activemq.xml, @@ -125,8 +126,6 @@ in { ''; }; - services.activemq.configurationDir = mkDefault "${activemq}/conf"; - }; } diff --git a/nixos/modules/services/computing/slurm/slurm.nix b/nixos/modules/services/computing/slurm/slurm.nix index cf00d8946557..ad8836f40094 100644 --- a/nixos/modules/services/computing/slurm/slurm.nix +++ b/nixos/modules/services/computing/slurm/slurm.nix @@ -37,6 +37,7 @@ in package = mkOption { type = types.package; default = pkgs.slurm-llnl; + defaultText = "pkgs.slurm-llnl"; example = literalExample "pkgs.slurm-llnl-full"; description = '' The packge to use for slurm binaries. diff --git a/nixos/modules/services/continuous-integration/jenkins/default.nix b/nixos/modules/services/continuous-integration/jenkins/default.nix index fb77586f6894..6fd39e68b1d9 100644 --- a/nixos/modules/services/continuous-integration/jenkins/default.nix +++ b/nixos/modules/services/continuous-integration/jenkins/default.nix @@ -92,11 +92,12 @@ in { type = with types; attrsOf str; description = '' Additional environment variables to be passed to the jenkins process. - As a base environment, jenkins receives NIX_PATH, SSL_CERT_FILE and - GIT_SSL_CAINFO from <option>environment.sessionVariables</option>, - NIX_REMOTE is set to "daemon" and JENKINS_HOME is set to - the value of <option>services.jenkins.home</option>. This option has - precedence and can be used to override those mentioned variables. + As a base environment, jenkins receives NIX_PATH from + <option>environment.sessionVariables</option>, NIX_REMOTE is set to + "daemon" and JENKINS_HOME is set to the value of + <option>services.jenkins.home</option>. + This option has precedence and can be used to override those + mentioned variables. ''; }; @@ -136,11 +137,7 @@ in { environment = let selectedSessionVars = - lib.filterAttrs (n: v: builtins.elem n - [ "NIX_PATH" - "SSL_CERT_FILE" - "GIT_SSL_CAINFO" - ]) + lib.filterAttrs (n: v: builtins.elem n [ "NIX_PATH" ]) config.environment.sessionVariables; in selectedSessionVars // diff --git a/nixos/modules/services/databases/postgresql.nix b/nixos/modules/services/databases/postgresql.nix index c2045a5859c5..31ffe51c11ef 100644 --- a/nixos/modules/services/databases/postgresql.nix +++ b/nixos/modules/services/databases/postgresql.nix @@ -158,7 +158,7 @@ in # Note: when changing the default, make it conditional on # ‘system.stateVersion’ to maintain compatibility with existing # systems! - mkDefault pkgs.postgresql94; + mkDefault (if versionAtLeast config.system.stateVersion "16.03" then pkgs.postgresql95 else pkgs.postgresql94); services.postgresql.authentication = mkAfter '' @@ -177,7 +177,7 @@ in users.extraGroups.postgres.gid = config.ids.gids.postgres; - environment.systemPackages = [postgresql]; + environment.systemPackages = [ postgresql ]; systemd.services.postgresql = { description = "PostgreSQL Server"; @@ -187,35 +187,37 @@ in environment.PGDATA = cfg.dataDir; - path = [ pkgs.su postgresql ]; + path = [ postgresql ]; preStart = '' - # Initialise the database. + # Create data directory. if ! test -e ${cfg.dataDir}/PG_VERSION; then - mkdir -m 0700 -p ${cfg.dataDir} - rm -f ${cfg.dataDir}/*.conf - if [ "$(id -u)" = 0 ]; then - chown -R postgres ${cfg.dataDir} - su -s ${pkgs.stdenv.shell} postgres -c 'initdb -U root' - else - # For non-root operation. - initdb - fi - # See postStart! - touch "${cfg.dataDir}/.first_startup" + mkdir -m 0700 -p ${cfg.dataDir} + rm -f ${cfg.dataDir}/*.conf + chown -R postgres:postgres ${cfg.dataDir} fi + ''; # */ + script = + '' + # Initialise the database. + if ! test -e ${cfg.dataDir}/PG_VERSION; then + initdb -U root + # See postStart! + touch "${cfg.dataDir}/.first_startup" + fi ln -sfn "${configFile}" "${cfg.dataDir}/postgresql.conf" ${optionalString (cfg.recoveryConfig != null) '' ln -sfn "${pkgs.writeText "recovery.conf" cfg.recoveryConfig}" \ "${cfg.dataDir}/recovery.conf" ''} - ''; # */ + + exec postgres ${toString flags} + ''; serviceConfig = - { ExecStart = "@${postgresql}/bin/postgres postgres ${toString flags}"; - ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + { ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; User = "postgres"; Group = "postgres"; PermissionsStartOnly = true; diff --git a/nixos/modules/services/hardware/acpid.nix b/nixos/modules/services/hardware/acpid.nix index 48b2b6be09ed..bb17c8859d84 100644 --- a/nixos/modules/services/hardware/acpid.nix +++ b/nixos/modules/services/hardware/acpid.nix @@ -4,111 +4,38 @@ with lib; let - acpiConfDir = pkgs.runCommand "acpi-events" {} - '' - mkdir -p $out - ${ - # Generate a configuration file for each event. (You can't have - # multiple events in one config file...) - let f = event: - '' - fn=$out/${event.name} - echo "event=${event.event}" > $fn - echo "action=${pkgs.writeScript "${event.name}.sh" event.action}" >> $fn - ''; - in lib.concatMapStrings f events - } - ''; - - events = [powerEvent lidEvent acEvent muteEvent volumeDownEvent volumeUpEvent cdPlayEvent cdNextEvent cdPrevEvent]; - - # Called when the power button is pressed. - powerEvent = - { name = "power-button"; + canonicalHandlers = { + powerEvent = { event = "button/power.*"; - action = - '' - #! ${pkgs.bash}/bin/sh - ${config.services.acpid.powerEventCommands} - ''; + action = config.services.acpid.powerEventCommands; }; - # Called when the laptop lid is opened/closed. - lidEvent = - { name = "lid"; + lidEvent = { event = "button/lid.*"; - action = - '' - #! ${pkgs.bash}/bin/sh - ${config.services.acpid.lidEventCommands} - ''; + action = config.services.acpid.lidEventCommands; }; - # Called when the AC power is connected or disconnected. - acEvent = - { name = "ac-power"; + acEvent = { event = "ac_adapter.*"; - action = - '' - #! ${pkgs.bash}/bin/sh - ${config.services.acpid.acEventCommands} - ''; + action = config.services.acpid.acEventCommands; }; - - muteEvent = { - name = "mute"; - event = "button/mute.*"; - action = '' - #! ${pkgs.bash}/bin/sh - ${config.services.acpid.muteCommands} - ''; - }; - - volumeDownEvent = { - name = "volume-down"; - event = "button/volumedown.*"; - action = '' - #! ${pkgs.bash}/bin/sh - ${config.services.acpid.volumeDownEventCommands} - ''; }; - volumeUpEvent = { - name = "volume-up"; - event = "button/volumeup.*"; - action = '' - #! ${pkgs.bash}/bin/sh - ${config.services.acpid.volumeUpEventCommands} - ''; - }; - - cdPlayEvent = { - name = "cd-play"; - event = "cd/play.*"; - action = '' - #! ${pkgs.bash}/bin/sh - ${config.services.acpid.cdPlayEventCommands} - ''; - }; - - cdNextEvent = { - name = "cd-next"; - event = "cd/next.*"; - action = '' - #! ${pkgs.bash}/bin/sh - ${config.services.acpid.cdNextEventCommands} - ''; - }; - - cdPrevEvent = { - name = "cd-prev"; - event = "cd/prev.*"; - action = '' - #! ${pkgs.bash}/bin/sh - ${config.services.acpid.cdPrevEventCommands} + acpiConfDir = pkgs.runCommand "acpi-events" {} + '' + mkdir -p $out + ${ + # Generate a configuration file for each event. (You can't have + # multiple events in one config file...) + let f = name: handler: + '' + fn=$out/${name} + echo "event=${handler.event}" > $fn + echo "action=${pkgs.writeScript "${name}.sh" (concatStringsSep "\n" [ "#! ${pkgs.bash}/bin/sh" handler.action ])}" >> $fn + ''; + in concatStringsSep "\n" (mapAttrsToList f (canonicalHandlers // config.services.acpid.handlers)) + } ''; - }; - in @@ -126,58 +53,45 @@ in description = "Whether to enable the ACPI daemon."; }; - powerEventCommands = mkOption { - type = types.lines; - default = ""; - description = "Shell commands to execute on a button/power.* event."; - }; + handlers = mkOption { + type = types.attrsOf (types.submodule { + options = { + event = mkOption { + type = types.str; + example = [ "button/power.*" "button/lid.*" "ac_adapter.*" "button/mute.*" "button/volumedown.*" "cd/play.*" "cd/next.*" ]; + description = "Event type."; + }; - lidEventCommands = mkOption { - type = types.lines; - default = ""; - description = "Shell commands to execute on a button/lid.* event."; - }; + action = mkOption { + type = types.lines; + description = "Shell commands to execute when the event is triggered."; + }; + }; + }); - acEventCommands = mkOption { - type = types.lines; - default = ""; - description = "Shell commands to execute on an ac_adapter.* event."; - }; - - muteCommands = mkOption { - type = types.lines; - default = ""; - description = "Shell commands to execute on an button/mute.* event."; - }; + description = "Event handlers."; + default = {}; + example = { mute = { event = "button/mute.*"; action = "amixer set Master toggle"; }; }; - volumeDownEventCommands = mkOption { - type = types.lines; - default = ""; - description = "Shell commands to execute on an button/volumedown.* event."; - }; - volumeUpEventCommands = mkOption { - type = types.lines; - default = ""; - description = "Shell commands to execute on an button/volumeup.* event."; }; - cdPlayEventCommands = mkOption { + powerEventCommands = mkOption { type = types.lines; default = ""; - description = "Shell commands to execute on an cd/play.* event."; + description = "Shell commands to execute on a button/power.* event."; }; - cdNextEventCommands = mkOption { + lidEventCommands = mkOption { type = types.lines; default = ""; - description = "Shell commands to execute on an cd/next.* event."; + description = "Shell commands to execute on a button/lid.* event."; }; - cdPrevEventCommands = mkOption { + acEventCommands = mkOption { type = types.lines; default = ""; - description = "Shell commands to execute on an cd/prev.* event."; + description = "Shell commands to execute on an ac_adapter.* event."; }; }; diff --git a/nixos/modules/services/hardware/bluetooth.nix b/nixos/modules/services/hardware/bluetooth.nix index 68d0406e63bd..fc95c4910bf7 100644 --- a/nixos/modules/services/hardware/bluetooth.nix +++ b/nixos/modules/services/hardware/bluetooth.nix @@ -1,6 +1,43 @@ { config, lib, pkgs, ... }: with lib; +let + bluez-bluetooth = if config.services.xserver.desktopManager.kde4.enable then pkgs.bluez else pkgs.bluez5; + + configBluez = { + description = "Bluetooth Service"; + serviceConfig = { + Type = "dbus"; + BusName = "org.bluez"; + ExecStart = "${bluez-bluetooth}/sbin/bluetoothd -n"; + }; + wantedBy = [ "bluetooth.target" ]; + }; + + configBluez5 = { + description = "Bluetooth Service"; + serviceConfig = { + Type = "dbus"; + BusName = "org.bluez"; + ExecStart = "${bluez-bluetooth}/sbin/bluetoothd -n"; + NotifyAccess="main"; + CapabilityBoundingSet="CAP_NET_ADMIN CAP_NET_BIND_SERVICE"; + LimitNPROC=1; + }; + wantedBy = [ "bluetooth.target" ]; + }; + + obexConfig = { + description = "Bluetooth OBEX service"; + serviceConfig = { + Type = "dbus"; + BusName = "org.bluez.obex"; + ExecStart = "${bluez-bluetooth}/sbin/obexd"; + }; + }; + + bluezConfig = if config.services.xserver.desktopManager.kde4.enable then configBluez else configBluez5; +in { @@ -16,26 +53,15 @@ with lib; }; - ###### implementation - + config = mkIf config.hardware.bluetooth.enable { - environment.systemPackages = [ pkgs.bluez pkgs.openobex pkgs.obexftp ]; - - services.udev.packages = [ pkgs.bluez ]; - - services.dbus.packages = [ pkgs.bluez ]; - - systemd.services."dbus-org.bluez" = { - description = "Bluetooth Service"; - serviceConfig = { - Type = "dbus"; - BusName = "org.bluez"; - ExecStart = "${pkgs.bluez}/sbin/bluetoothd -n"; - }; - wantedBy = [ "bluetooth.target" ]; - }; + environment.systemPackages = [ bluez-bluetooth pkgs.openobex pkgs.obexftp ]; + services.udev.packages = [ bluez-bluetooth ]; + services.dbus.packages = [ bluez-bluetooth ]; + systemd.services."dbus-org.bluez" = bluezConfig; + systemd.services."dbus-org.bluez.obex" = obexConfig; }; diff --git a/nixos/modules/services/hardware/sane.nix b/nixos/modules/services/hardware/sane.nix index 56504cd2361d..a34037403123 100644 --- a/nixos/modules/services/hardware/sane.nix +++ b/nixos/modules/services/hardware/sane.nix @@ -21,7 +21,13 @@ in hardware.sane.enable = mkOption { type = types.bool; default = false; - description = "Enable support for SANE scanners."; + description = '' + Enable support for SANE scanners. + + <note><para> + Users in the "scanner" group will gain access to the scanner. + </para></note> + ''; }; hardware.sane.snapshot = mkOption { @@ -33,7 +39,14 @@ in hardware.sane.extraBackends = mkOption { type = types.listOf types.path; default = []; - description = "Packages providing extra SANE backends to enable."; + description = '' + Packages providing extra SANE backends to enable. + + <note><para> + The example contains the package for HP scanners. + </para></note> + ''; + example = literalExample "[ pkgs.hplipWithPlugin ]"; }; hardware.sane.configDir = mkOption { diff --git a/nixos/modules/services/hardware/udev.nix b/nixos/modules/services/hardware/udev.nix index 107e1012b88d..8e883ed7775f 100644 --- a/nixos/modules/services/hardware/udev.nix +++ b/nixos/modules/services/hardware/udev.nix @@ -13,7 +13,13 @@ let extraUdevRules = pkgs.writeTextFile { name = "extra-udev-rules"; text = cfg.extraRules; - destination = "/etc/udev/rules.d/10-local.rules"; + destination = "/etc/udev/rules.d/99-local.rules"; + }; + + extraHwdbFile = pkgs.writeTextFile { + name = "extra-hwdb-file"; + text = cfg.extraHwdb; + destination = "/etc/udev/hwdb.d/99-local.hwdb"; }; nixosRules = '' @@ -55,7 +61,9 @@ let --replace \"/sbin/modprobe \"${config.system.sbin.modprobe}/sbin/modprobe \ --replace \"/sbin/mdadm \"${pkgs.mdadm}/sbin/mdadm \ --replace \"/sbin/blkid \"${pkgs.utillinux}/sbin/blkid \ - --replace \"/bin/mount \"${pkgs.utillinux}/bin/mount + --replace \"/bin/mount \"${pkgs.utillinux}/bin/mount \ + --replace /usr/bin/readlink ${pkgs.coreutils}/bin/readlink \ + --replace /usr/bin/basename ${pkgs.coreutils}/bin/basename done echo -n "Checking that all programs called by relative paths in udev rules exist in ${udev}/lib/udev... " @@ -86,10 +94,30 @@ let done echo "OK" - echo "Consider fixing the following udev rules:" - for i in ${toString cfg.packages}; do - grep -l '\(RUN+\|IMPORT{program}\)="\(/usr\)\?/s\?bin' $i/*/udev/rules.d/* || true - done + filesToFixup="$(for i in "$out"/*; do + grep -l '\B\(/usr\)\?/s\?bin' "$i" || : + done)" + + if [ -n "$filesToFixup" ]; then + echo "Consider fixing the following udev rules:" + echo "$filesToFixup" | while read localFile; do + remoteFile="origin unknown" + for i in ${toString cfg.packages}; do + for j in "$i"/*/udev/rules.d/*; do + [ -e "$out/$(basename "$j")" ] || continue + [ "$(basename "$j")" = "$(basename "$localFile")" ] || continue + remoteFile="originally from $j" + break 2 + done + done + refs="$( + grep -o '\B\(/usr\)\?/s\?bin/[^ "]\+' "$localFile" \ + | sed -e ':r;N;''${s/\n/ and /;br};s/\n/, /g;br' + )" + echo "$localFile ($remoteFile) contains references to $refs." + done + exit 1 + fi ${optionalString config.networking.usePredictableInterfaceNames '' cp ${./80-net-setup-link.rules} $out/80-net-setup-link.rules @@ -104,6 +132,27 @@ let ''; # */ }; + hwdbBin = stdenv.mkDerivation { + name = "hwdb.bin"; + + preferLocalBuild = true; + allowSubstitutes = false; + + buildCommand = '' + mkdir -p etc/udev/hwdb.d + for i in ${toString ([udev] ++ cfg.packages)}; do + echo "Adding hwdb files for package $i" + for j in $i/{etc,lib}/udev/hwdb.d/*; do + ln -s $j etc/udev/hwdb.d/$(basename $j) + done + done + + echo "Generating hwdb database..." + ${udev}/bin/udevadm hwdb --update --root=$(pwd) + mv etc/udev/hwdb.bin $out + ''; + }; + # Udev has a 512-character limit for ENV{PATH}, so create a symlink # tree to work around this. udevPath = pkgs.buildEnv { @@ -163,8 +212,23 @@ in type = types.lines; description = '' Additional <command>udev</command> rules. They'll be written - into file <filename>10-local.rules</filename>. Thus they are - read before all other rules. + into file <filename>99-local.rules</filename>. Thus they are + read and applied after all other rules. + ''; + }; + + extraHwdb = mkOption { + default = ""; + example = '' + evdev:input:b0003v05AFp8277* + KEYBOARD_KEY_70039=leftalt + KEYBOARD_KEY_700e2=leftctrl + ''; + type = types.lines; + description = '' + Additional <command>hwdb</command> files. They'll be written + into file <filename>10-local.hwdb</filename>. Thus they are + read before all other files. ''; }; @@ -216,7 +280,7 @@ in services.udev.extraRules = nixosRules; - services.udev.packages = [ extraUdevRules ]; + services.udev.packages = [ extraUdevRules extraHwdbFile ]; services.udev.path = [ pkgs.coreutils pkgs.gnused pkgs.gnugrep pkgs.utillinux udev ]; @@ -224,6 +288,9 @@ in [ { source = udevRules; target = "udev/rules.d"; } + { source = hwdbBin; + target = "udev/hwdb.bin"; + } ]; system.requiredKernelConfig = with config.lib.kernelConfig; [ @@ -241,13 +308,6 @@ in echo "" > /proc/sys/kernel/hotplug fi - # Regenerate the hardware database /var/lib/udev/hwdb.bin - # whenever systemd changes. - if [ ! -e /var/lib/udev/prev-systemd -o "$(readlink /var/lib/udev/prev-systemd)" != ${config.systemd.package} ]; then - echo "regenerating udev hardware database..." - ${config.systemd.package}/bin/udevadm hwdb --update && ln -sfn ${config.systemd.package} /var/lib/udev/prev-systemd - fi - # Allow the kernel to find our firmware. if [ -e /sys/module/firmware_class/parameters/path ]; then echo -n "${config.hardware.firmware}/lib/firmware" > /sys/module/firmware_class/parameters/path @@ -256,6 +316,7 @@ in systemd.services.systemd-udevd = { environment.MODULE_DIR = "/run/booted-system/kernel-modules/lib/modules"; + restartTriggers = cfg.packages; }; }; diff --git a/nixos/modules/services/mail/dovecot.nix b/nixos/modules/services/mail/dovecot.nix index 36bdcaca47a3..3935c14dc8cc 100644 --- a/nixos/modules/services/mail/dovecot.nix +++ b/nixos/modules/services/mail/dovecot.nix @@ -9,16 +9,11 @@ let baseDir = "/run/dovecot2"; stateDir = "/var/lib/dovecot"; - protocols = concatStrings [ - (optionalString cfg.enableImap "imap") - (optionalString cfg.enablePop3 "pop3") - (optionalString cfg.enableLmtp "lmtp") - ]; - dovecotConf = concatStrings [ '' base_dir = ${baseDir} - protocols = ${protocols} + protocols = ${concatStringsSep " " cfg.protocols} + sendmail_path = /var/setuid-wrappers/sendmail '' (if isNull cfg.sslServerCert then '' @@ -33,6 +28,8 @@ let '' default_internal_user = ${cfg.user} + ${optionalString (cfg.mailUser != null) "mail_uid = ${cfg.mailUser}"} + ${optionalString (cfg.mailGroup != null) "mail_gid = ${cfg.mailGroup}"} mail_location = ${cfg.mailLocation} @@ -57,11 +54,17 @@ let } '') + (optionalString (cfg.sieveScripts != {}) '' + plugin { + ${concatStringsSep "\n" (mapAttrsToList (to: from: "sieve_${to} = ${stateDir}/sieve/${to}") cfg.sieveScripts)} + } + '') + cfg.extraConfig ]; modulesDir = pkgs.symlinkJoin "dovecot-modules" - (map (module: "${module}/lib/dovecot") cfg.modules); + (map (pkg: "${pkg}/lib/dovecot") ([ dovecotPkg ] ++ map (module: module.override { dovecot = dovecotPkg; }) cfg.modules)); in { @@ -87,6 +90,12 @@ in description = "Start the LMTP listener (when Dovecot is enabled)."; }; + protocols = mkOption { + type = types.listOf types.str; + default = [ ]; + description = "Additional listeners to start when Dovecot is enabled."; + }; + package = mkOption { type = types.package; default = pkgs.dovecot22; @@ -129,13 +138,25 @@ in ''; }; + mailUser = mkOption { + type = types.nullOr types.str; + default = null; + description = "Default user to store mail for virtual users."; + }; + + mailGroup = mkOption { + type = types.nullOr types.str; + default = null; + description = "Default group to store mail for virtual users."; + }; + modules = mkOption { type = types.listOf types.package; default = []; example = literalExample "[ pkgs.dovecot_pigeonhole ]"; description = '' Symlinks the contents of lib/dovecot of every given package into - /var/lib/dovecot/modules. This will make the given modules available + /etc/dovecot/modules. This will make the given modules available if a dovecot package with the module_dir patch applied (like pkgs.dovecot22, the default) is being used. ''; @@ -162,7 +183,13 @@ in enablePAM = mkOption { type = types.bool; default = true; - description = "Wether to create a own Dovecot PAM service and configure PAM user logins."; + description = "Whether to create a own Dovecot PAM service and configure PAM user logins."; + }; + + sieveScripts = mkOption { + type = types.attrsOf types.path; + default = {}; + description = "Sieve scripts to be executed. Key is a sequence, e.g. 'before2', 'after' etc."; }; showPAMFailure = mkOption { @@ -177,23 +204,31 @@ in security.pam.services.dovecot2 = mkIf cfg.enablePAM {}; + services.dovecot2.protocols = + optional cfg.enableImap "imap" + ++ optional cfg.enablePop3 "pop3" + ++ optional cfg.enableLmtp "lmtp"; + users.extraUsers = [ - { name = cfg.user; - uid = config.ids.uids.dovecot2; - description = "Dovecot user"; - group = cfg.group; - } { name = "dovenull"; uid = config.ids.uids.dovenull2; description = "Dovecot user for untrusted logins"; group = cfg.group; } - ]; + ] ++ optional (cfg.user == "dovecot2") + { name = "dovecot2"; + uid = config.ids.uids.dovecot2; + description = "Dovecot user"; + group = cfg.group; + }; + + users.extraGroups = optional (cfg.group == "dovecot2") + { name = "dovecot2"; + gid = config.ids.gids.dovecot2; + }; - users.extraGroups = singleton { - name = cfg.group; - gid = config.ids.gids.dovecot2; - }; + environment.etc."dovecot/modules".source = modulesDir; + environment.etc."dovecot/dovecot.conf".source = cfg.configFile; systemd.services.dovecot2 = { description = "Dovecot IMAP/POP3 server"; @@ -201,26 +236,38 @@ in after = [ "keys.target" "network.target" ]; wants = [ "keys.target" ]; wantedBy = [ "multi-user.target" ]; - - preStart = '' - mkdir -p "${baseDir}/login" - chown -R ${cfg.user}:${cfg.group} "${baseDir}" - rm -f "${stateDir}/modules" - ln -s "${modulesDir}" "${stateDir}/modules" - ''; + restartTriggers = [ cfg.configFile ]; serviceConfig = { - ExecStart = "${dovecotPkg}/sbin/dovecot -F -c ${cfg.configFile}"; + ExecStart = "${dovecotPkg}/sbin/dovecot -F"; + ExecReload = "${dovecotPkg}/sbin/doveadm reload"; Restart = "on-failure"; RestartSec = "1s"; StartLimitInterval = "1min"; + RuntimeDirectory = [ "dovecot2" ]; }; + + preStart = '' + rm -rf ${stateDir}/sieve + '' + optionalString (cfg.sieveScripts != {}) '' + mkdir -p ${stateDir}/sieve + ${concatStringsSep "\n" (mapAttrsToList (to: from: '' + if [ -d '${from}' ]; then + mkdir '${stateDir}/sieve/${to}' + cp "${from}/"*.sieve '${stateDir}/sieve/${to}' + else + cp '${from}' '${stateDir}/sieve/${to}' + fi + ${pkgs.dovecot_pigeonhole}/bin/sievec '${stateDir}/sieve/${to}' + '') cfg.sieveScripts)} + chown -R '${cfg.mailUser}:${cfg.mailGroup}' '${stateDir}/sieve' + ''; }; environment.systemPackages = [ dovecotPkg ]; assertions = [ - { assertion = cfg.enablePop3 || cfg.enableImap; + { assertion = intersectLists cfg.protocols [ "pop3" "imap" ] != []; message = "dovecot needs at least one of the IMAP or POP3 listeners enabled"; } { assertion = isNull cfg.sslServerCert == isNull cfg.sslServerKey diff --git a/nixos/modules/services/mail/dspam.nix b/nixos/modules/services/mail/dspam.nix index 10352ba6abcc..46e6f216b21e 100644 --- a/nixos/modules/services/mail/dspam.nix +++ b/nixos/modules/services/mail/dspam.nix @@ -19,7 +19,10 @@ let SystemLog on UserLog on - ${optionalString (cfg.domainSocket != null) ''ServerDomainSocketPath "${cfg.domainSocket}"''} + ${optionalString (cfg.domainSocket != null) '' + ServerDomainSocketPath "${cfg.domainSocket}" + ClientHost "${cfg.domainSocket}" + ''} ${cfg.extraConfig} ''; @@ -108,7 +111,11 @@ in { User = cfg.user; Group = cfg.group; RuntimeDirectory = optional (cfg.domainSocket == defaultSock) "dspam"; + RuntimeDirectoryMode = optional (cfg.domainSocket == defaultSock) "0750"; PermissionsStartOnly = true; + # DSPAM segfaults on just about every error + Restart = "on-failure"; + RestartSec = "1s"; }; preStart = '' @@ -136,7 +143,7 @@ in { restartTriggers = [ cfgfile ]; serviceConfig = { - ExecStart = "${dspam}/bin/dspam_maintenance"; + ExecStart = "${dspam}/bin/dspam_maintenance --verbose"; Type = "oneshot"; User = cfg.user; Group = cfg.group; diff --git a/nixos/modules/services/mail/opendkim.nix b/nixos/modules/services/mail/opendkim.nix index 1cdae9cb6548..af996758f41f 100644 --- a/nixos/modules/services/mail/opendkim.nix +++ b/nixos/modules/services/mail/opendkim.nix @@ -49,7 +49,12 @@ in { domains = mkOption { type = types.str; - description = "Local domains set; messages from them are signed, not verified."; + default = "csl:${config.networking.hostName}"; + example = "csl:example.com,mydomain.net"; + description = '' + Local domains set (see <literal>opendkim(8)</literal> for more information on datasets). + Messages from them are signed, not verified. + ''; }; keyFile = mkOption { @@ -77,8 +82,6 @@ in { config = mkIf cfg.enable { - services.opendkim.domains = mkDefault "csl:${config.networking.hostName}"; - users.extraUsers = optionalAttrs (cfg.user == "opendkim") (singleton { name = "opendkim"; group = cfg.group; diff --git a/nixos/modules/services/mail/opensmtpd.nix b/nixos/modules/services/mail/opensmtpd.nix index a1cfd84365a2..42a1244cde57 100644 --- a/nixos/modules/services/mail/opensmtpd.nix +++ b/nixos/modules/services/mail/opensmtpd.nix @@ -9,6 +9,11 @@ let conf = writeText "smtpd.conf" cfg.serverConfiguration; args = concatStringsSep " " cfg.extraServerArgs; + sendmail = pkgs.runCommand "opensmtpd-sendmail" {} '' + mkdir -p $out/bin + ln -s ${opensmtpd}/sbin/smtpctl $out/bin/sendmail + ''; + in { ###### interface @@ -23,6 +28,15 @@ in { description = "Whether to enable the OpenSMTPD server."; }; + addSendmailToSystemPath = mkOption { + type = types.bool; + default = true; + description = '' + Whether to add OpenSMTPD's sendmail binary to the + system path or not. + ''; + }; + extraServerArgs = mkOption { type = types.listOf types.str; default = []; @@ -64,7 +78,7 @@ in { ###### implementation - config = mkIf config.services.opensmtpd.enable { + config = mkIf cfg.enable { users.extraGroups = { smtpd.gid = config.ids.gids.smtpd; smtpq.gid = config.ids.gids.smtpq; @@ -98,9 +112,6 @@ in { environment.OPENSMTPD_PROC_PATH = "${procEnv}/libexec/opensmtpd"; }; - environment.systemPackages = [ (pkgs.runCommand "opensmtpd-sendmail" {} '' - mkdir -p $out/bin - ln -s ${opensmtpd}/sbin/smtpctl $out/bin/sendmail - '') ]; + environment.systemPackages = mkIf cfg.addSendmailToSystemPath [ sendmail ]; }; } diff --git a/nixos/modules/services/mail/postfix.nix b/nixos/modules/services/mail/postfix.nix index 6c5d7e92702b..404cdf0f564b 100644 --- a/nixos/modules/services/mail/postfix.nix +++ b/nixos/modules/services/mail/postfix.nix @@ -13,6 +13,18 @@ let haveTransport = cfg.transport != ""; haveVirtual = cfg.virtual != ""; + clientAccess = + if (cfg.dnsBlacklistOverrides != "") + then [ "check_client_access hash:/etc/postfix/client_access" ] + else []; + + dnsBl = + if (cfg.dnsBlacklists != []) + then [ (concatStringsSep ", " (map (s: "reject_rbl_client " + s) cfg.dnsBlacklists)) ] + else []; + + clientRestrictions = concatStringsSep ", " (clientAccess ++ dnsBl); + mainCf = '' compatibility_level = 2 @@ -20,6 +32,23 @@ let mail_owner = ${user} default_privs = nobody + # NixOS specific locations + data_directory = /var/lib/postfix/data + queue_directory = /var/lib/postfix/queue + + # Default location of everything in package + meta_directory = ${pkgs.postfix}/etc/postfix + command_directory = ${pkgs.postfix}/bin + sample_directory = /etc/postfix + newaliases_path = ${pkgs.postfix}/bin/newaliases + mailq_path = ${pkgs.postfix}/bin/mailq + readme_directory = no + sendmail_path = ${pkgs.postfix}/bin/sendmail + daemon_directory = ${pkgs.postfix}/libexec/postfix + manpage_directory = ${pkgs.postfix}/share/man + html_directory = ${pkgs.postfix}/share/postfix/doc/html + shlib_directory = no + '' + optionalString config.networking.enableIPv6 '' inet_protocols = all @@ -87,6 +116,9 @@ let + optionalString haveVirtual '' virtual_alias_maps = hash:/etc/postfix/virtual '' + + optionalString (cfg.dnsBlacklists != []) '' + smtpd_client_restrictions = ${clientRestrictions} + '' + cfg.extraConfig; masterCf = '' @@ -144,6 +176,7 @@ let aliasesFile = pkgs.writeText "postfix-aliases" aliases; virtualFile = pkgs.writeText "postfix-virtual" cfg.virtual; + checkClientAccessFile = pkgs.writeText "postfix-check-client-access" cfg.dnsBlacklistOverrides; mainCfFile = pkgs.writeText "postfix-main.cf" mainCf; masterCfFile = pkgs.writeText "postfix-master.cf" masterCf; transportFile = pkgs.writeText "postfix-transport" cfg.transport; @@ -349,6 +382,17 @@ in "; }; + dnsBlacklists = mkOption { + default = []; + type = with types; listOf string; + description = "dns blacklist servers to use with smtpd_client_restrictions"; + }; + + dnsBlacklistOverrides = mkOption { + default = ""; + description = "contents of check_client_access for overriding dnsBlacklists"; + }; + extraMasterConf = mkOption { type = types.lines; default = ""; @@ -435,31 +479,35 @@ in mkdir -p /var/lib mv /var/postfix /var/lib/postfix fi - mkdir -p /var/lib/postfix/data /var/lib/postfix/queue/{pid,public,maildrop} - chown -R ${user}:${group} /var/lib/postfix - chown root /var/lib/postfix/queue - chown root /var/lib/postfix/queue/pid - chgrp -R ${setgidGroup} /var/lib/postfix/queue/{public,maildrop} - chmod 770 /var/lib/postfix/queue/{public,maildrop} + # All permissions set according ${pkgs.postfix}/etc/postfix/postfix-files script + mkdir -p /var/lib/postfix /var/lib/postfix/queue/{pid,public,maildrop} + chmod 0755 /var/lib/postfix + chown root:root /var/lib/postfix rm -rf /var/lib/postfix/conf mkdir -p /var/lib/postfix/conf + chmod 0755 /var/lib/postfix/conf + ln -sf ${pkgs.postfix}/etc/postfix/postfix-files /var/lib/postfix/conf/postfix-files ln -sf ${mainCfFile} /var/lib/postfix/conf/main.cf ln -sf ${masterCfFile} /var/lib/postfix/conf/master.cf + ${concatStringsSep "\n" (mapAttrsToList (to: from: '' ln -sf ${from} /var/lib/postfix/conf/${to} - postalias /var/lib/postfix/conf/${to} + ${pkgs.postfix}/bin/postalias /var/lib/postfix/conf/${to} '') cfg.aliasFiles)} ${concatStringsSep "\n" (mapAttrsToList (to: from: '' ln -sf ${from} /var/lib/postfix/conf/${to} - postmap /var/lib/postfix/conf/${to} + ${pkgs.postfix}/bin/postmap /var/lib/postfix/conf/${to} '') cfg.mapFiles)} mkdir -p /var/spool/mail chown root:root /var/spool/mail chmod a+rwxt /var/spool/mail ln -sf /var/spool/mail /var/ + + #Finally delegate to postfix checking remain directories in /var/lib/postfix and set permissions on them + ${pkgs.postfix}/bin/postfix set-permissions config_directory=/var/lib/postfix/conf ''; }; } @@ -473,6 +521,9 @@ in (mkIf haveVirtual { services.postfix.mapFiles."virtual" = virtualFile; }) + (mkIf (cfg.dnsBlacklists != []) { + services.postfix.mapFiles."client_access" = checkClientAccessFile; + }) ]); } diff --git a/nixos/modules/services/mail/postsrsd.nix b/nixos/modules/services/mail/postsrsd.nix index 36a0f8218d88..68a4c1012064 100644 --- a/nixos/modules/services/mail/postsrsd.nix +++ b/nixos/modules/services/mail/postsrsd.nix @@ -95,7 +95,11 @@ in { preStart = '' if [ ! -e "${cfg.secretsFile}" ]; then echo "WARNING: secrets file not found, autogenerating!" - mkdir -p -m750 "$(dirname "${cfg.secretsFile}")" + DIR="$(dirname "${cfg.secretsFile}")" + if [ ! -d "$DIR" ]; then + mkdir -p -m750 "$DIR" + chown "${cfg.user}:${cfg.group}" "$DIR" + fi dd if=/dev/random bs=18 count=1 | base64 > "${cfg.secretsFile}" chmod 600 "${cfg.secretsFile}" fi diff --git a/nixos/modules/services/misc/bepasty.nix b/nixos/modules/services/misc/bepasty.nix index 12671cb1b6cd..5bda73ab64f0 100644 --- a/nixos/modules/services/misc/bepasty.nix +++ b/nixos/modules/services/misc/bepasty.nix @@ -103,9 +103,13 @@ in after = [ "network.target" ]; restartIfChanged = true; - environment = { + environment = let + penv = python.buildEnv.override { + extraLibs = [ bepasty gevent ]; + }; + in { BEPASTY_CONFIG = "${server.workDir}/bepasty-${name}.conf"; - PYTHONPATH= "${bepasty}/lib/${python.libPrefix}/site-packages:${gevent}/lib/${python.libPrefix}/site-packages"; + PYTHONPATH= "${penv}/${python.sitePackages}/"; }; serviceConfig = { diff --git a/nixos/modules/services/misc/cfdyndns.nix b/nixos/modules/services/misc/cfdyndns.nix new file mode 100644 index 000000000000..69a33d0b8c1b --- /dev/null +++ b/nixos/modules/services/misc/cfdyndns.nix @@ -0,0 +1,70 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let + cfg = config.services.cfdyndns; +in +{ + options = { + services.cfdyndns = { + enable = mkEnableOption "Cloudflare Dynamic DNS Client"; + + email = mkOption { + type = types.str; + description = '' + The email address to use to authenticate to CloudFlare. + ''; + }; + + apikey = mkOption { + type = types.str; + description = '' + The API Key to use to authenticate to CloudFlare. + ''; + }; + + records = mkOption { + default = []; + example = [ "host.tld" ]; + type = types.listOf types.str; + description = '' + The records to update in CloudFlare. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + systemd.services.cfdyndns = { + description = "CloudFlare Dynamic DNS Client"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + startAt = "5 minutes"; + serviceConfig = { + Type = "simple"; + User = config.ids.uids.cfdyndns; + Group = config.ids.gids.cfdyndns; + ExecStart = "/bin/sh -c '${pkgs.cfdyndns}/bin/cfdyndns'"; + }; + environment = { + CLOUDFLARE_EMAIL="${cfg.email}"; + CLOUDFLARE_APIKEY="${cfg.apikey}"; + CLOUDFLARE_RECORDS="${concatStringsSep "," cfg.records}"; + }; + }; + + users.extraUsers = { + cfdyndns = { + group = "cfdyndns"; + uid = config.ids.uids.cfdyndns; + }; + }; + + users.extraGroups = { + cfdyndns = { + gid = config.ids.gids.cfdyndns; + }; + }; + }; +} diff --git a/nixos/modules/services/misc/defaultUnicornConfig.rb b/nixos/modules/services/misc/defaultUnicornConfig.rb index 81abaf336dc0..84622622db70 100644 --- a/nixos/modules/services/misc/defaultUnicornConfig.rb +++ b/nixos/modules/services/misc/defaultUnicornConfig.rb @@ -187,7 +187,6 @@ working_directory ENV["GITLAB_PATH"] pid ENV["UNICORN_PATH"] + "/tmp/pids/unicorn.pid" listen ENV["UNICORN_PATH"] + "/tmp/sockets/gitlab.socket", :backlog => 1024 -listen "127.0.0.1:8080", :tcp_nopush => true timeout 60 diff --git a/nixos/modules/services/misc/gammu-smsd.nix b/nixos/modules/services/misc/gammu-smsd.nix new file mode 100644 index 000000000000..91047ead4364 --- /dev/null +++ b/nixos/modules/services/misc/gammu-smsd.nix @@ -0,0 +1,253 @@ +{ pkgs, lib, config, ... }: + +with lib; +let + cfg = config.services.gammu-smsd; + + configFile = pkgs.writeText "gammu-smsd.conf" '' + [gammu] + Device = ${cfg.device.path} + Connection = ${cfg.device.connection} + SynchronizeTime = ${if cfg.device.synchronizeTime then "yes" else "no"} + LogFormat = ${cfg.log.format} + ${if (cfg.device.pin != null) then "PIN = ${cfg.device.pin}" else ""} + ${cfg.extraConfig.gammu} + + + [smsd] + LogFile = ${cfg.log.file} + Service = ${cfg.backend.service} + + ${optionalString (cfg.backend.service == "files") '' + InboxPath = ${cfg.backend.files.inboxPath} + OutboxPath = ${cfg.backend.files.outboxPath} + SentSMSPath = ${cfg.backend.files.sentSMSPath} + ErrorSMSPath = ${cfg.backend.files.errorSMSPath} + ''} + + ${optionalString (cfg.backend.service == "sql" && cfg.backend.sql.driver == "sqlite") '' + Driver = ${cfg.backend.sql.driver} + DBDir = ${cfg.backend.sql.database} + ''} + + ${optionalString (cfg.backend.service == "sql" && cfg.backend.sql.driver == "native_pgsql") ( + with cfg.backend; '' + Driver = ${sql.driver} + ${if (sql.database!= null) then "Database = ${sql.database}" else ""} + ${if (sql.host != null) then "Host = ${sql.host}" else ""} + ${if (sql.user != null) then "User = ${sql.user}" else ""} + ${if (sql.password != null) then "Password = ${sql.password}" else ""} + '')} + + ${cfg.extraConfig.smsd} + ''; + + initDBDir = "share/doc/gammu/examples/sql"; + + gammuPackage = with cfg.backend; (pkgs.gammu.override { + dbiSupport = (service == "sql" && sql.driver == "sqlite"); + postgresSupport = (service == "sql" && sql.driver == "native_pgsql"); + }); + +in { + options = { + services.gammu-smsd = { + + enable = mkEnableOption "gammu-smsd daemon"; + + user = mkOption { + type = types.str; + default = "smsd"; + description = "User that has access to the device"; + }; + + device = { + path = mkOption { + type = types.path; + description = "Device node or address of the phone"; + example = "/dev/ttyUSB2"; + }; + + group = mkOption { + type = types.str; + default = "root"; + description = "Owner group of the device"; + example = "dialout"; + }; + + connection = mkOption { + type = types.str; + default = "at"; + description = "Protocol which will be used to talk to the phone"; + }; + + synchronizeTime = mkOption { + type = types.bool; + default = true; + description = "Whether to set time from computer to the phone during starting connection"; + }; + + pin = mkOption { + type = types.nullOr types.str; + default = null; + description = "PIN code for the simcard"; + }; + }; + + + log = { + file = mkOption { + type = types.str; + default = "syslog"; + description = "Path to file where information about communication will be stored"; + }; + + format = mkOption { + type = types.enum [ "nothing" "text" "textall" "textalldate" "errors" "errorsdate" "binary" ]; + default = "errors"; + description = "Determines what will be logged to the LogFile"; + }; + }; + + + extraConfig = { + gammu = mkOption { + type = types.lines; + default = ""; + description = "Extra config lines to be added into [gammu] section"; + }; + + + smsd = mkOption { + type = types.lines; + default = ""; + description = "Extra config lines to be added into [smsd] section"; + }; + }; + + + backend = { + service = mkOption { + type = types.enum [ "null" "files" "sql" ]; + default = "null"; + description = "Service to use to store sms data."; + }; + + files = { + inboxPath = mkOption { + type = types.path; + default = "/var/spool/sms/inbox/"; + description = "Where the received SMSes are stored"; + }; + + outboxPath = mkOption { + type = types.path; + default = "/var/spool/sms/outbox/"; + description = "Where SMSes to be sent should be placed"; + }; + + sentSMSPath = mkOption { + type = types.path; + default = "/var/spool/sms/sent/"; + description = "Where the transmitted SMSes are placed"; + }; + + errorSMSPath = mkOption { + type = types.path; + default = "/var/spool/sms/error/"; + description = "Where SMSes with error in transmission is placed"; + }; + }; + + sql = { + driver = mkOption { + type = types.enum [ "native_mysql" "native_pgsql" "odbc" "dbi" ]; + description = "DB driver to use"; + }; + + sqlDialect = mkOption { + type = types.nullOr types.str; + default = null; + description = "SQL dialect to use (odbc driver only)"; + }; + + database = mkOption { + type = types.str; + default = null; + description = "Database name to store sms data"; + }; + + host = mkOption { + type = types.str; + default = "localhost"; + description = "Database server address"; + }; + + user = mkOption { + type = types.nullOr types.str; + default = null; + description = "User name used for connection to the database"; + }; + + password = mkOption { + type = types.nullOr types.str; + default = null; + description = "User password used for connetion to the database"; + }; + }; + }; + }; + }; + + config = mkIf cfg.enable { + users.extraUsers.${cfg.user} = { + description = "gammu-smsd user"; + uid = config.ids.uids.gammu-smsd; + extraGroups = [ "${cfg.device.group}" ]; + }; + + environment.systemPackages = with cfg.backend; [ gammuPackage ] + ++ optionals (service == "sql" && sql.driver == "sqlite") [ pkgs.sqlite ]; + + systemd.services.gammu-smsd = { + description = "gammu-smsd daemon"; + + wantedBy = [ "multi-user.target" ]; + + wants = with cfg.backend; [ ] + ++ optionals (service == "sql" && sql.driver == "native_pgsql") [ "postgresql.service" ]; + + preStart = with cfg.backend; + + optionalString (service == "files") (with files; '' + mkdir -m 755 -p ${inboxPath} ${outboxPath} ${sentSMSPath} ${errorSMSPath} + chown ${cfg.user} -R ${inboxPath} + chown ${cfg.user} -R ${outboxPath} + chown ${cfg.user} -R ${sentSMSPath} + chown ${cfg.user} -R ${errorSMSPath} + '') + + optionalString (service == "sql" && sql.driver == "sqlite") '' + cat "${gammuPackage}/${initDBDir}/sqlite.sql" \ + | ${pkgs.sqlite}/bin/sqlite3 ${sql.database} + '' + + (let execPsql = extraArgs: concatStringsSep " " [ + (optionalString (sql.password != null) "PGPASSWORD=${sql.password}") + "${config.services.postgresql.package}/bin/psql" + (optionalString (sql.host != null) "-h ${sql.host}") + (optionalString (sql.user != null) "-U ${sql.user}") + "$extraArgs" + "${sql.database}" + ]; in optionalString (service == "sql" && sql.driver == "native_pgsql") '' + echo '\i '"${gammuPackage}/${initDBDir}/pgsql.sql" | ${execPsql ""} + ''); + + serviceConfig = { + User = "${cfg.user}"; + Group = "${cfg.device.group}"; + PermissionsStartOnly = true; + ExecStart = "${gammuPackage}/bin/gammu-smsd -c ${configFile}"; + }; + + }; + }; +} diff --git a/nixos/modules/services/misc/gitit.nix b/nixos/modules/services/misc/gitit.nix index ab4d385ba165..befd8c628f16 100644 --- a/nixos/modules/services/misc/gitit.nix +++ b/nixos/modules/services/misc/gitit.nix @@ -35,7 +35,6 @@ let }; haskellPackages = mkOption { - type = types.attrsOf types.package; default = pkgs.haskellPackages; defaultText = "pkgs.haskellPackages"; example = literalExample "pkgs.haskell.packages.ghc784"; diff --git a/nixos/modules/services/misc/gitlab.nix b/nixos/modules/services/misc/gitlab.nix index 949357ab20f4..cc50bfbea531 100644 --- a/nixos/modules/services/misc/gitlab.nix +++ b/nixos/modules/services/misc/gitlab.nix @@ -7,10 +7,13 @@ with lib; let cfg = config.services.gitlab; - ruby = pkgs.gitlab.ruby; + ruby = cfg.packages.gitlab.ruby; bundler = pkgs.bundler; - gemHome = "${pkgs.gitlab.env}/${ruby.gemPath}"; + gemHome = "${cfg.packages.gitlab.env}/${ruby.gemPath}"; + + gitlabSocket = "${cfg.statePath}/tmp/sockets/gitlab.socket"; + pathUrlQuote = url: replaceStrings ["/"] ["%2F"] url; databaseYml = '' production: @@ -21,14 +24,15 @@ let username: ${cfg.databaseUsername} encoding: utf8 ''; + gitlabShellYml = '' - user: gitlab - gitlab_url: "http://${cfg.host}:${toString cfg.port}/" + user: ${cfg.user} + gitlab_url: "http+unix://${pathUrlQuote gitlabSocket}" http_settings: self_signed_cert: false - repos_path: "${cfg.stateDir}/repositories" - secret_file: "${cfg.stateDir}/config/gitlab_shell_secret" - log_file: "${cfg.stateDir}/log/gitlab-shell.log" + repos_path: "${cfg.statePath}/repositories" + secret_file: "${cfg.statePath}/config/gitlab_shell_secret" + log_file: "${cfg.statePath}/log/gitlab-shell.log" redis: bin: ${pkgs.redis}/bin/redis-cli host: 127.0.0.1 @@ -37,33 +41,102 @@ let namespace: resque:gitlab ''; + gitlabConfig = { + # These are the default settings from config/gitlab.example.yml + production = flip recursiveUpdate cfg.extraConfig { + gitlab = { + host = cfg.host; + port = cfg.port; + https = cfg.https; + user = cfg.user; + email_enabled = true; + email_display_name = "GitLab"; + email_reply_to = "noreply@localhost"; + default_theme = 2; + default_projects_features = { + issues = true; + merge_requests = true; + wiki = true; + snippets = false; + builds = true; + }; + }; + artifacts = { + enabled = true; + }; + lfs = { + enabled = true; + }; + gravatar = { + enabled = true; + }; + cron_jobs = { + stuck_ci_builds_worker = { + cron = "0 0 * * *"; + }; + }; + gitlab_ci = { + builds_path = "${cfg.statePath}/builds"; + }; + ldap = { + enabled = false; + }; + omniauth = { + enabled = false; + }; + shared = { + path = "${cfg.statePath}/shared"; + }; + backup = { + path = "${cfg.backupPath}"; + }; + gitlab_shell = { + path = "${cfg.packages.gitlab-shell}"; + repos_path = "${cfg.statePath}/repositories"; + hooks_path = "${cfg.statePath}/shell/hooks"; + secret_file = "${cfg.statePath}/config/gitlab_shell_secret"; + upload_pack = true; + receive_pack = true; + }; + git = { + bin_path = "git"; + max_size = 20971520; # 20MB + timeout = 10; + }; + extra = {}; + }; + }; + + gitlabEnv = { + HOME = "${cfg.statePath}/home"; + GEM_HOME = gemHome; + BUNDLE_GEMFILE = "${cfg.packages.gitlab}/share/gitlab/Gemfile"; + UNICORN_PATH = "${cfg.statePath}/"; + GITLAB_PATH = "${cfg.packages.gitlab}/share/gitlab/"; + GITLAB_STATE_PATH = "${cfg.statePath}"; + GITLAB_UPLOADS_PATH = "${cfg.statePath}/uploads"; + GITLAB_LOG_PATH = "${cfg.statePath}/log"; + GITLAB_SHELL_PATH = "${cfg.packages.gitlab-shell}"; + GITLAB_SHELL_CONFIG_PATH = "${cfg.statePath}/shell/config.yml"; + GITLAB_SHELL_SECRET_PATH = "${cfg.statePath}/config/gitlab_shell_secret"; + GITLAB_SHELL_HOOKS_PATH = "${cfg.statePath}/shell/hooks"; + RAILS_ENV = "production"; + }; + unicornConfig = builtins.readFile ./defaultUnicornConfig.rb; gitlab-runner = pkgs.stdenv.mkDerivation rec { name = "gitlab-runner"; - buildInputs = [ pkgs.gitlab pkgs.bundler pkgs.makeWrapper ]; + buildInputs = [ cfg.packages.gitlab bundler pkgs.makeWrapper ]; phases = "installPhase fixupPhase"; buildPhase = ""; installPhase = '' mkdir -p $out/bin - makeWrapper ${bundler}/bin/bundle $out/bin/gitlab-runner\ - --set RAKEOPT '"-f ${pkgs.gitlab}/share/gitlab/Rakefile"'\ - --set GEM_HOME '${gemHome}'\ - --set UNICORN_PATH "${cfg.stateDir}/"\ - --set GITLAB_PATH "${pkgs.gitlab}/share/gitlab/"\ - --set GITLAB_APPLICATION_LOG_PATH "${cfg.stateDir}/log/application.log"\ - --set GITLAB_SATELLITES_PATH "${cfg.stateDir}/satellites"\ - --set GITLAB_SHELL_PATH "${pkgs.gitlab-shell}"\ - --set GITLAB_REPOSITORIES_PATH "${cfg.stateDir}/repositories"\ - --set GITLAB_SHELL_HOOKS_PATH "${cfg.stateDir}/shell/hooks"\ - --set BUNDLE_GEMFILE "${pkgs.gitlab}/share/gitlab/Gemfile"\ - --set GITLAB_EMAIL_FROM "${cfg.emailFrom}"\ - --set GITLAB_SHELL_CONFIG_PATH "${cfg.stateDir}/shell/config.yml"\ - --set GITLAB_SHELL_SECRET_PATH "${cfg.stateDir}/config/gitlab_shell_secret"\ - --set GITLAB_HOST "${cfg.host}"\ - --set GITLAB_PORT "${toString cfg.port}"\ - --set GITLAB_BACKUP_PATH "${cfg.backupPath}"\ - --set RAILS_ENV "production" + makeWrapper ${bundler}/bin/bundle $out/bin/gitlab-runner \ + ${concatStrings (mapAttrsToList (name: value: "--set ${name} '\"${value}\"' ") gitlabEnv)} \ + --set GITLAB_CONFIG_PATH '"${cfg.statePath}/config"' \ + --set PATH '"${pkgs.nodejs}/bin:${pkgs.gzip}/bin:${config.services.postgresql.package}/bin:$PATH"' \ + --set RAKEOPT '"-f ${cfg.packages.gitlab}/share/gitlab/Rakefile"' ''; }; @@ -79,13 +152,25 @@ in { ''; }; - satelliteDir = mkOption { - type = types.str; - default = "/var/gitlab/git-satellites"; - description = "Gitlab directory to store checked out git trees requires for operation."; + packages.gitlab = mkOption { + type = types.package; + default = pkgs.gitlab; + description = "Reference to the gitlab package"; + }; + + packages.gitlab-shell = mkOption { + type = types.package; + default = pkgs.gitlab-shell; + description = "Reference to the gitlab-shell package"; + }; + + packages.gitlab-workhorse = mkOption { + type = types.package; + default = pkgs.gitlab-workhorse; + description = "Reference to the gitlab-workhorse package"; }; - stateDir = mkOption { + statePath = mkOption { type = types.str; default = "/var/gitlab/state"; description = "Gitlab state directory, logs are stored here."; @@ -93,7 +178,7 @@ in { backupPath = mkOption { type = types.str; - default = cfg.stateDir + "/backup"; + default = cfg.statePath + "/backup"; description = "Gitlab path for backups."; }; @@ -136,14 +221,67 @@ in { port = mkOption { type = types.int; default = 8080; - description = "Gitlab server listening port."; + description = '' + Gitlab server port for copy-paste URLs, e.g. 80 or 443 if you're + service over https. + ''; + }; + + https = mkOption { + type = types.bool; + default = false; + description = "Whether gitlab prints URLs with https as scheme."; + }; + + user = mkOption { + type = types.str; + default = "gitlab"; + description = "User to run gitlab and all related services."; + }; + + group = mkOption { + type = types.str; + default = "gitlab"; + description = "Group to run gitlab and all related services."; + }; + + initialRootEmail = mkOption { + type = types.str; + default = "admin@local.host"; + description = '' + Initial email address of the root account if this is a new install. + ''; + }; + + initialRootPassword = mkOption { + type = types.str; + default = "UseNixOS!"; + description = '' + Initial password of the root account if this is a new install. + ''; + }; + + extraConfig = mkOption { + type = types.attrs; + default = {}; + example = { + gitlab = { + default_projects_features = { + builds = false; + }; + }; + }; + description = '' + Extra options to be merged into config/gitlab.yml as nix + attribute set. + ''; }; }; }; config = mkIf cfg.enable { - environment.systemPackages = [ pkgs.git gitlab-runner pkgs.gitlab-shell ]; + environment.systemPackages = [ pkgs.git gitlab-runner cfg.packages.gitlab-shell ]; assertions = [ { assertion = cfg.databasePassword != ""; @@ -159,39 +297,24 @@ in { services.postfix.enable = mkDefault true; users.extraUsers = [ - { name = "gitlab"; - group = "gitlab"; - home = "${cfg.stateDir}/home"; + { name = cfg.user; + group = cfg.group; + home = "${cfg.statePath}/home"; shell = "${pkgs.bash}/bin/bash"; uid = config.ids.uids.gitlab; - } ]; + } + ]; users.extraGroups = [ - { name = "gitlab"; + { name = cfg.group; gid = config.ids.gids.gitlab; - } ]; + } + ]; systemd.services.gitlab-sidekiq = { after = [ "network.target" "redis.service" ]; wantedBy = [ "multi-user.target" ]; - environment.HOME = "${cfg.stateDir}/home"; - environment.GEM_HOME = gemHome; - environment.UNICORN_PATH = "${cfg.stateDir}/"; - environment.GITLAB_PATH = "${pkgs.gitlab}/share/gitlab/"; - environment.GITLAB_APPLICATION_LOG_PATH = "${cfg.stateDir}/log/application.log"; - environment.GITLAB_SATELLITES_PATH = "${cfg.stateDir}/satellites"; - environment.GITLAB_SHELL_PATH = "${pkgs.gitlab-shell}"; - environment.GITLAB_REPOSITORIES_PATH = "${cfg.stateDir}/repositories"; - environment.GITLAB_SHELL_HOOKS_PATH = "${cfg.stateDir}/shell/hooks"; - environment.BUNDLE_GEMFILE = "${pkgs.gitlab}/share/gitlab/Gemfile"; - environment.GITLAB_EMAIL_FROM = "${cfg.emailFrom}"; - environment.GITLAB_SHELL_CONFIG_PATH = "${cfg.stateDir}/shell/config.yml"; - environment.GITLAB_SHELL_SECRET_PATH = "${cfg.stateDir}/config/gitlab_shell_secret"; - environment.GITLAB_HOST = "${cfg.host}"; - environment.GITLAB_PORT = "${toString cfg.port}"; - environment.GITLAB_DATABASE_HOST = "${cfg.databaseHost}"; - environment.GITLAB_DATABASE_PASSWORD = "${cfg.databasePassword}"; - environment.RAILS_ENV = "production"; + environment = gitlabEnv; path = with pkgs; [ config.services.postgresql.package gitAndTools.git @@ -201,116 +324,131 @@ in { ]; serviceConfig = { Type = "simple"; - User = "gitlab"; - Group = "gitlab"; + User = cfg.user; + Group = cfg.group; TimeoutSec = "300"; - WorkingDirectory = "${pkgs.gitlab}/share/gitlab"; - ExecStart="${bundler}/bin/bundle exec \"sidekiq -q post_receive -q mailer -q system_hook -q project_web_hook -q gitlab_shell -q common -q default -e production -P ${cfg.stateDir}/tmp/sidekiq.pid\""; + WorkingDirectory = "${cfg.packages.gitlab}/share/gitlab"; + ExecStart="${bundler}/bin/bundle exec \"sidekiq -q post_receive -q mailer -q system_hook -q project_web_hook -q gitlab_shell -q common -q default -e production -P ${cfg.statePath}/tmp/sidekiq.pid\""; }; }; - systemd.services.gitlab-git-http-server = { + systemd.services.gitlab-workhorse = { after = [ "network.target" "gitlab.service" ]; wantedBy = [ "multi-user.target" ]; - environment.HOME = "${cfg.stateDir}/home"; + environment.HOME = gitlabEnv.HOME; + environment.GITLAB_SHELL_CONFIG_PATH = gitlabEnv.GITLAB_SHELL_CONFIG_PATH; path = with pkgs; [ gitAndTools.git openssh ]; + preStart = '' + mkdir -p /run/gitlab + chown ${cfg.user}:${cfg.group} /run/gitlab + ''; serviceConfig = { + PermissionsStartOnly = true; # preStart must be run as root Type = "simple"; - User = "gitlab"; - Group = "gitlab"; + User = cfg.user; + Group = cfg.group; TimeoutSec = "300"; - ExecStart = "${pkgs.gitlab-git-http-server}/bin/gitlab-git-http-server -listenUmask 0 -listenNetwork unix -listenAddr ${cfg.stateDir}/tmp/sockets/gitlab-git-http-server.socket -authBackend http://localhost:8080 ${cfg.stateDir}/repositories"; + ExecStart = + "${cfg.packages.gitlab-workhorse}/bin/gitlab-workhorse " + + "-listenUmask 0 " + + "-listenNetwork unix " + + "-listenAddr /run/gitlab/gitlab-workhorse.socket " + + "-authSocket ${gitlabSocket} " + + "-documentRoot ${cfg.packages.gitlab}/share/gitlab/public"; }; }; systemd.services.gitlab = { after = [ "network.target" "postgresql.service" "redis.service" ]; wantedBy = [ "multi-user.target" ]; - environment.HOME = "${cfg.stateDir}/home"; - environment.GEM_HOME = gemHome; - environment.UNICORN_PATH = "${cfg.stateDir}/"; - environment.GITLAB_PATH = "${pkgs.gitlab}/share/gitlab/"; - environment.GITLAB_APPLICATION_LOG_PATH = "${cfg.stateDir}/log/application.log"; - environment.GITLAB_SATELLITES_PATH = "${cfg.stateDir}/satellites"; - environment.GITLAB_SHELL_PATH = "${pkgs.gitlab-shell}"; - environment.GITLAB_SHELL_CONFIG_PATH = "${cfg.stateDir}/shell/config.yml"; - environment.GITLAB_SHELL_SECRET_PATH = "${cfg.stateDir}/config/gitlab_shell_secret"; - environment.GITLAB_REPOSITORIES_PATH = "${cfg.stateDir}/repositories"; - environment.GITLAB_SHELL_HOOKS_PATH = "${cfg.stateDir}/shell/hooks"; - environment.BUNDLE_GEMFILE = "${pkgs.gitlab}/share/gitlab/Gemfile"; - environment.GITLAB_EMAIL_FROM = "${cfg.emailFrom}"; - environment.GITLAB_HOST = "${cfg.host}"; - environment.GITLAB_PORT = "${toString cfg.port}"; - environment.GITLAB_DATABASE_HOST = "${cfg.databaseHost}"; - environment.GITLAB_DATABASE_PASSWORD = "${cfg.databasePassword}"; - environment.RAILS_ENV = "production"; + environment = gitlabEnv; path = with pkgs; [ config.services.postgresql.package gitAndTools.git - ruby openssh nodejs ]; preStart = '' - # TODO: use env vars - mkdir -p ${cfg.stateDir} - mkdir -p ${cfg.stateDir}/log - mkdir -p ${cfg.stateDir}/satellites - mkdir -p ${cfg.stateDir}/repositories - mkdir -p ${cfg.stateDir}/shell/hooks - mkdir -p ${cfg.stateDir}/tmp/pids - mkdir -p ${cfg.stateDir}/tmp/sockets - rm -rf ${cfg.stateDir}/config - mkdir -p ${cfg.stateDir}/config - # TODO: What exactly is gitlab-shell doing with the secret? - tr -dc _A-Z-a-z-0-9 < /dev/urandom | head -c 20 > ${cfg.stateDir}/config/gitlab_shell_secret - mkdir -p ${cfg.stateDir}/home/.ssh - touch ${cfg.stateDir}/home/.ssh/authorized_keys - - cp -rf ${pkgs.gitlab}/share/gitlab/config ${cfg.stateDir}/ - cp ${pkgs.gitlab}/share/gitlab/VERSION ${cfg.stateDir}/VERSION + mkdir -p ${cfg.backupPath} + mkdir -p ${cfg.statePath}/builds + mkdir -p ${cfg.statePath}/repositories + mkdir -p ${gitlabConfig.production.shared.path}/artifacts + mkdir -p ${gitlabConfig.production.shared.path}/lfs-objects + mkdir -p ${cfg.statePath}/log + mkdir -p ${cfg.statePath}/shell + mkdir -p ${cfg.statePath}/tmp/pids + mkdir -p ${cfg.statePath}/tmp/sockets + + rm -rf ${cfg.statePath}/config ${cfg.statePath}/shell/hooks + mkdir -p ${cfg.statePath}/config ${cfg.statePath}/shell - ln -fs ${pkgs.writeText "database.yml" databaseYml} ${cfg.stateDir}/config/database.yml - ln -fs ${pkgs.writeText "unicorn.rb" unicornConfig} ${cfg.stateDir}/config/unicorn.rb - - chown -R gitlab:gitlab ${cfg.stateDir}/ - chmod -R 755 ${cfg.stateDir}/ + # TODO: What exactly is gitlab-shell doing with the secret? + tr -dc _A-Z-a-z-0-9 < /dev/urandom | head -c 20 > ${cfg.statePath}/config/gitlab_shell_secret + + # The uploads directory is hardcoded somewhere deep in rails. It is + # symlinked in the gitlab package to /run/gitlab/uploads to make it + # configurable + mkdir -p /run/gitlab + mkdir -p ${cfg.statePath}/uploads + ln -sf ${cfg.statePath}/uploads /run/gitlab/uploads + chown -R ${cfg.user}:${cfg.group} /run/gitlab + + # Prepare home directory + mkdir -p ${gitlabEnv.HOME}/.ssh + touch ${gitlabEnv.HOME}/.ssh/authorized_keys + chown -R ${cfg.user}:${cfg.group} ${gitlabEnv.HOME}/ + chmod -R u+rwX,go-rwx+X ${gitlabEnv.HOME}/ + + cp -rf ${cfg.packages.gitlab}/share/gitlab/config.dist/* ${cfg.statePath}/config + ln -sf ${cfg.statePath}/config /run/gitlab/config + cp ${cfg.packages.gitlab}/share/gitlab/VERSION ${cfg.statePath}/VERSION + + # JSON is a subset of YAML + ln -fs ${pkgs.writeText "gitlab.yml" (builtins.toJSON gitlabConfig)} ${cfg.statePath}/config/gitlab.yml + ln -fs ${pkgs.writeText "database.yml" databaseYml} ${cfg.statePath}/config/database.yml + ln -fs ${pkgs.writeText "unicorn.rb" unicornConfig} ${cfg.statePath}/config/unicorn.rb + + chown -R ${cfg.user}:${cfg.group} ${cfg.statePath}/ + chmod -R ug+rwX,o-rwx+X ${cfg.statePath}/ + + # Install the shell required to push repositories + ln -fs ${pkgs.writeText "config.yml" gitlabShellYml} "$GITLAB_SHELL_CONFIG_PATH" + ln -fs ${cfg.packages.gitlab-shell}/hooks "$GITLAB_SHELL_HOOKS_PATH" + ${cfg.packages.gitlab-shell}/bin/install if [ "${cfg.databaseHost}" = "127.0.0.1" ]; then - if ! test -e "${cfg.stateDir}/db-created"; then + if ! test -e "${cfg.statePath}/db-created"; then psql postgres -c "CREATE ROLE gitlab WITH LOGIN NOCREATEDB NOCREATEROLE NOCREATEUSER ENCRYPTED PASSWORD '${cfg.databasePassword}'" ${config.services.postgresql.package}/bin/createdb --owner gitlab gitlab || true - touch "${cfg.stateDir}/db-created" + touch "${cfg.statePath}/db-created" - # force=yes disables the manual-interaction yes/no prompt - # which breaks without an stdin. - force=yes ${bundler}/bin/bundle exec rake -f ${pkgs.gitlab}/share/gitlab/Rakefile gitlab:setup RAILS_ENV=production + # The gitlab:setup task is horribly broken somehow, these two tasks will do the same for setting up the initial database + ${gitlab-runner}/bin/gitlab-runner exec rake db:migrate RAILS_ENV=production + ${gitlab-runner}/bin/gitlab-runner exec rake db:seed_fu RAILS_ENV=production \ + GITLAB_ROOT_PASSWORD="${cfg.initialRootPassword}" GITLAB_ROOT_EMAIL="${cfg.initialRootEmail}"; fi fi - ${bundler}/bin/bundle exec rake -f ${pkgs.gitlab}/share/gitlab/Rakefile db:migrate RAILS_ENV=production - # Install the shell required to push repositories - ln -fs ${pkgs.writeText "config.yml" gitlabShellYml} ${cfg.stateDir}/shell/config.yml - export GITLAB_SHELL_CONFIG_PATH=""${cfg.stateDir}/shell/config.yml - ${pkgs.gitlab-shell}/bin/install + # Always do the db migrations just to be sure the database is up-to-date + ${gitlab-runner}/bin/gitlab-runner exec rake db:migrate RAILS_ENV=production - # Change permissions in the last step because some of the - # intermediary scripts like to create directories as root. - chown -R gitlab:gitlab ${cfg.stateDir}/ - chmod -R 755 ${cfg.stateDir}/ + # Change permissions in the last step because some of the + # intermediary scripts like to create directories as root. + chown -R ${cfg.user}:${cfg.group} ${cfg.statePath} + chmod -R u+rwX,go-rwx+X ${cfg.statePath} ''; serviceConfig = { PermissionsStartOnly = true; # preStart must be run as root Type = "simple"; - User = "gitlab"; - Group = "gitlab"; + User = cfg.user; + Group = cfg.group; TimeoutSec = "300"; - WorkingDirectory = "${pkgs.gitlab}/share/gitlab"; - ExecStart="${bundler}/bin/bundle exec \"unicorn -c ${cfg.stateDir}/config/unicorn.rb -E production\""; + WorkingDirectory = "${cfg.packages.gitlab}/share/gitlab"; + ExecStart="${bundler}/bin/bundle exec \"unicorn -c ${cfg.statePath}/config/unicorn.rb -E production\""; }; }; diff --git a/nixos/modules/services/misc/gitlab.xml b/nixos/modules/services/misc/gitlab.xml new file mode 100644 index 000000000000..b630fe421130 --- /dev/null +++ b/nixos/modules/services/misc/gitlab.xml @@ -0,0 +1,103 @@ +<chapter xmlns="http://docbook.org/ns/docbook" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:xi="http://www.w3.org/2001/XInclude" + version="5.0" + xml:id="module-services-gitlab"> + +<title>Gitlab</title> + +<para>Gitlab is a feature-rich git hosting service.</para> + +<section><title>Prerequisites</title> + +<para>The gitlab service exposes only an Unix socket at +<literal>/run/gitlab/gitlab-workhorse.socket</literal>. You need to configure a +webserver to proxy HTTP requests to the socket.</para> + +<para>For instance, this could be used for Nginx: + +<programlisting> +services.nginx.httpConfig = '' + server { + server_name git.example.com; + listen 443 ssl spdy; + listen [::]:443 ssl spdy; + + ssl_certificate /var/lib/acme/git.example.com/fullchain.pem; + ssl_certificate_key /var/lib/acme/git.example.com/key.pem; + + location / { + proxy_http_version 1.1; + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Ssl on; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + proxy_pass http://unix:/run/gitlab/gitlab-workhorse.socket; + } + } +''; +</programlisting> +</para> + +</section> + +<section><title>Configuring</title> + +<para>Gitlab depends on both PostgreSQL and Redis and will automatically enable +both services. In the case of PostgreSQL, a database and a role will be created. +</para> + +<para>The default state dir is /var/gitlab/state. This is where all data like +the repositories and uploads will be stored.</para> + +<para>A basic configuration could look like this: + +<programlisting> +services.gitlab = { + enable = true; + databasePassword = "eXaMpl3"; + initialRootPassword = "UseNixOS!"; + https = true; + host = "git.example.com"; + port = 443; + user = "git"; + group = "git"; + extraConfig = { + gitlab = { + default_projects_features = { builds = false; }; + }; + }; +}; +</programlisting> +</para> + +<para>Refer to <xref linkend="ch-options" /> for all available configuration +options for the <literal>services.gitlab</literal> module.</para> + +</section> + +<section><title>Maintenance</title> + +<para>You can run all Gitlab related commands like rake tasks with +<literal>gitlab-runner</literal> which will be available on the system +when gitlab is enabled. You will have to run the commands as the user that +you configured to run gitlab.</para> + +<para>For instance, to backup a Gitlab instance: + +<programlisting> +$ sudo -u git -H gitlab-runner exec rake gitlab:backup:create +</programlisting> + +A list of all availabe rake tasks can be obtained by running: + +<programlisting> +$ sudo -u git -H gitlab-runner exec rake -T +</programlisting> +</para> + +</section> + +</chapter> diff --git a/nixos/modules/services/misc/ihaskell.nix b/nixos/modules/services/misc/ihaskell.nix index 1927922909ee..d0e9b839e754 100644 --- a/nixos/modules/services/misc/ihaskell.nix +++ b/nixos/modules/services/misc/ihaskell.nix @@ -6,7 +6,6 @@ let cfg = config.services.ihaskell; ihaskell = pkgs.ihaskell.override { - inherit (cfg.haskellPackages) ihaskell ghcWithPackages; packages = self: cfg.extraPackages self; }; @@ -22,7 +21,6 @@ in }; haskellPackages = mkOption { - type = types.attrsOf types.package; default = pkgs.haskellPackages; defaultText = "pkgs.haskellPackages"; example = literalExample "pkgs.haskell.packages.ghc784"; diff --git a/nixos/modules/services/misc/matrix-synapse.nix b/nixos/modules/services/misc/matrix-synapse.nix index 27c5a38e6b88..0ae0516769c0 100644 --- a/nixos/modules/services/misc/matrix-synapse.nix +++ b/nixos/modules/services/misc/matrix-synapse.nix @@ -61,6 +61,7 @@ in { package = mkOption { type = types.package; default = pkgs.matrix-synapse; + defaultText = "pkgs.matrix-synapse"; description = '' Overridable attribute of the matrix synapse server package to use. ''; diff --git a/nixos/modules/services/misc/nix-daemon.nix b/nixos/modules/services/misc/nix-daemon.nix index 60e826bafd1e..b0e4bf106d31 100644 --- a/nixos/modules/services/misc/nix-daemon.nix +++ b/nixos/modules/services/misc/nix-daemon.nix @@ -364,9 +364,11 @@ in ++ optionals cfg.distributedBuilds [ pkgs.gzip ]; environment = cfg.envVars - // { CURL_CA_BUNDLE = "/etc/ssl/certs/ca-bundle.crt"; } + // { CURL_CA_BUNDLE = "/etc/ssl/certs/ca-certificates.crt"; } // config.networking.proxy.envVars; + unitConfig.RequiresMountsFor = "/nix/store"; + serviceConfig = { Nice = cfg.daemonNiceLevel; IOSchedulingPriority = cfg.daemonIONiceLevel; diff --git a/nixos/modules/services/misc/nixos-manual.nix b/nixos/modules/services/misc/nixos-manual.nix index 3e1f53e79f3e..37ea339300d4 100644 --- a/nixos/modules/services/misc/nixos-manual.nix +++ b/nixos/modules/services/misc/nixos-manual.nix @@ -17,16 +17,32 @@ let nixpkgs.system = config.nixpkgs.system; }; - eval = evalModules { - modules = [ versionModule ] ++ baseModules; - args = (config._module.args) // { modules = [ ]; }; - }; - + /* For the purpose of generating docs, evaluate options with each derivation + in `pkgs` (recursively) replaced by a fake with path "\${pkgs.attribute.path}". + It isn't perfect, but it seems to cover a vast majority of use cases. + Caveat: even if the package is reached by a different means, + the path above will be shown and not e.g. `${config.services.foo.package}`. */ manual = import ../../../doc/manual { inherit pkgs; version = config.system.nixosVersion; revision = config.system.nixosRevision; - options = eval.options; + options = + let + scrubbedEval = evalModules { + modules = [ versionModule ] ++ baseModules; + args = (config._module.args) // { modules = [ ]; }; + specialArgs = { pkgs = scrubDerivations "pkgs" pkgs; }; + }; + scrubDerivations = namePrefix: pkgSet: mapAttrs + (name: value: + let wholeName = "${namePrefix}.${name}"; in + if isAttrs value then + scrubDerivations wholeName value + // (optionalAttrs (isDerivation value) { outPath = "\${${wholeName}}"; }) + else value + ) + pkgSet; + in scrubbedEval.options; }; entry = "${manual.manual}/share/doc/nixos/index.html"; @@ -72,7 +88,8 @@ in }; services.nixosManual.ttyNumber = mkOption { - default = "8"; + type = types.int; + default = 8; description = '' Virtual console on which to show the manual. ''; @@ -80,6 +97,7 @@ in services.nixosManual.browser = mkOption { type = types.path; + default = "${pkgs.w3m-nox}/bin/w3m"; description = '' Browser used to show the manual. ''; @@ -96,7 +114,7 @@ in [ manual.manual help ] ++ optional config.programs.man.enable manual.manpages; - boot.extraTTYs = mkIf cfg.showManual ["tty${cfg.ttyNumber}"]; + boot.extraTTYs = mkIf cfg.showManual ["tty${toString cfg.ttyNumber}"]; systemd.services = optionalAttrs cfg.showManual { "nixos-manual" = @@ -106,7 +124,7 @@ in { ExecStart = "${cfg.browser} ${entry}"; StandardInput = "tty"; StandardOutput = "tty"; - TTYPath = "/dev/tty${cfg.ttyNumber}"; + TTYPath = "/dev/tty${toString cfg.ttyNumber}"; TTYReset = true; TTYVTDisallocate = true; Restart = "always"; @@ -117,8 +135,6 @@ in services.mingetty.helpLine = mkIf cfg.showManual "\nPress <Alt-F${toString cfg.ttyNumber}> for the NixOS manual."; - services.nixosManual.browser = mkDefault "${pkgs.w3m-nox}/bin/w3m"; - }; } diff --git a/nixos/modules/services/misc/octoprint.nix b/nixos/modules/services/misc/octoprint.nix new file mode 100644 index 000000000000..9cf46345c228 --- /dev/null +++ b/nixos/modules/services/misc/octoprint.nix @@ -0,0 +1,120 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.octoprint; + + cfgUpdate = pkgs.writeText "octoprint-config.yaml" (builtins.toJSON { + plugins.cura.cura_engine = "${pkgs.curaengine}/bin/CuraEngine"; + server.host = cfg.host; + server.port = cfg.port; + webcam.ffmpeg = "${pkgs.ffmpeg}/bin/ffmpeg"; + }); + + pluginsEnv = pkgs.python.buildEnv.override { + extraLibs = cfg.plugins pkgs.octoprint-plugins; + }; + +in +{ + ##### interface + + options = { + + services.octoprint = { + + enable = mkEnableOption "OctoPrint, web interface for 3D printers"; + + host = mkOption { + type = types.str; + default = "0.0.0.0"; + description = '' + Host to bind OctoPrint to. + ''; + }; + + port = mkOption { + type = types.int; + default = 5000; + description = '' + Port to bind OctoPrint to. + ''; + }; + + user = mkOption { + type = types.str; + default = "octoprint"; + description = "User for the daemon."; + }; + + group = mkOption { + type = types.str; + default = "octoprint"; + description = "Group for the daemon."; + }; + + stateDir = mkOption { + type = types.path; + default = "/var/lib/octoprint"; + description = "State directory of the daemon."; + }; + + plugins = mkOption { + #type = types.functionTo (types.listOf types.package); + default = plugins: []; + defaultText = "plugins: []"; + example = literalExample "plugins: [ m3d-fio ]"; + description = "Additional plugins."; + }; + + }; + + }; + + ##### implementation + + config = mkIf cfg.enable { + + users.extraUsers = optionalAttrs (cfg.user == "octoprint") (singleton + { name = "octoprint"; + group = cfg.group; + uid = config.ids.uids.octoprint; + }); + + users.extraGroups = optionalAttrs (cfg.group == "octoprint") (singleton + { name = "octoprint"; + gid = config.ids.gids.octoprint; + }); + + systemd.services.octoprint = { + description = "OctoPrint, web interface for 3D printers"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + path = [ pluginsEnv ]; + environment.PYTHONPATH = makeSearchPath pkgs.python.sitePackages [ pluginsEnv ]; + + preStart = '' + mkdir -p "${cfg.stateDir}" + if [ -e "${cfg.stateDir}/config.yaml" ]; then + ${pkgs.yaml-merge}/bin/yaml-merge "${cfg.stateDir}/config.yaml" "${cfgUpdate}" > "${cfg.stateDir}/config.yaml.tmp" + mv "${cfg.stateDir}/config.yaml.tmp" "${cfg.stateDir}/config.yaml" + else + cp "${cfgUpdate}" "${cfg.stateDir}/config.yaml" + chmod 600 "${cfg.stateDir}/config.yaml" + fi + chown -R ${cfg.user}:${cfg.group} "${cfg.stateDir}" + ''; + + serviceConfig = { + ExecStart = "${pkgs.octoprint}/bin/octoprint -b ${cfg.stateDir}"; + User = cfg.user; + Group = cfg.group; + PermissionsStartOnly = true; + }; + }; + + }; + +} diff --git a/nixos/modules/services/misc/plex.nix b/nixos/modules/services/misc/plex.nix index fb62351365ed..875771dfa37f 100644 --- a/nixos/modules/services/misc/plex.nix +++ b/nixos/modules/services/misc/plex.nix @@ -58,6 +58,7 @@ in package = mkOption { type = types.package; default = pkgs.plex; + defaultText = "pkgs.plex"; description = '' The Plex package to use. Plex subscribers may wish to use their own package here, pointing to subscriber-only server versions. diff --git a/nixos/modules/services/misc/spice-vdagentd.nix b/nixos/modules/services/misc/spice-vdagentd.nix new file mode 100644 index 000000000000..f8133394ffd3 --- /dev/null +++ b/nixos/modules/services/misc/spice-vdagentd.nix @@ -0,0 +1,30 @@ +{ config, pkgs, lib, ... }: + +with lib; +let + cfg = config.services.spice-vdagentd; +in +{ + options = { + services.spice-vdagentd = { + enable = mkEnableOption "Spice guest vdagent daemon"; + }; + }; + + config = mkIf cfg.enable { + + environment.systemPackages = [ pkgs.spice-vdagent ]; + + systemd.services.spice-vdagentd = { + description = "spice-vdagent daemon"; + wantedBy = [ "graphical.target" ]; + preStart = '' + mkdir -p "/var/run/spice-vdagentd/" + ''; + serviceConfig = { + Type = "forking"; + ExecStart = "/bin/sh -c '${pkgs.spice-vdagent}/bin/spice-vdagentd'"; + }; + }; + }; +} diff --git a/nixos/modules/services/misc/subsonic.nix b/nixos/modules/services/misc/subsonic.nix index 5a33aa33b260..c1ebe418f727 100644 --- a/nixos/modules/services/misc/subsonic.nix +++ b/nixos/modules/services/misc/subsonic.nix @@ -97,6 +97,7 @@ in transcoders = mkOption { type = types.listOf types.path; + default = [ "${pkgs.ffmpeg.bin}/bin/ffmpeg" ]; description = '' List of paths to transcoder executables that should be accessible from Subsonic. Symlinks will be created to each executable inside @@ -152,8 +153,5 @@ in }; users.extraGroups.subsonic.gid = config.ids.gids.subsonic; - - services.subsonic.transcoders = mkDefault [ "${pkgs.ffmpeg.bin}/bin/ffmpeg" ]; - }; } diff --git a/nixos/modules/services/misc/sundtek.nix b/nixos/modules/services/misc/sundtek.nix index 8438ef79904f..e3234518c940 100644 --- a/nixos/modules/services/misc/sundtek.nix +++ b/nixos/modules/services/misc/sundtek.nix @@ -23,7 +23,7 @@ in Type = "oneshot"; ExecStart = '' ${pkgs.sundtek}/bin/mediasrv -d -v -p ${pkgs.sundtek}/bin ;\ - ${pkgs.sundtek}/bin/mediaclient --start=5 --wait-for-devices + ${pkgs.sundtek}/bin/mediaclient --start --wait-for-devices ''; ExecStop = "${pkgs.sundtek}/bin/mediaclient --shutdown"; RemainAfterExit = true; diff --git a/nixos/modules/services/monitoring/bosun.nix b/nixos/modules/services/monitoring/bosun.nix index 46273fc12187..51d38e8db4de 100644 --- a/nixos/modules/services/monitoring/bosun.nix +++ b/nixos/modules/services/monitoring/bosun.nix @@ -129,17 +129,19 @@ in { description = "bosun metrics collector (part of Bosun)"; wantedBy = [ "multi-user.target" ]; - preStart = - '' - mkdir -p `dirname ${cfg.stateFile}`; - touch ${cfg.stateFile} - touch ${cfg.stateFile}.tmp + preStart = '' + mkdir -p "$(dirname "${cfg.stateFile}")"; + touch "${cfg.stateFile}" + touch "${cfg.stateFile}.tmp" + + mkdir -p "${cfg.ledisDir}"; if [ "$(id -u)" = 0 ]; then - chown ${cfg.user}:${cfg.group} ${cfg.stateFile} - chown ${cfg.user}:${cfg.group} ${cfg.stateFile}.tmp + chown ${cfg.user}:${cfg.group} "${cfg.stateFile}" + chown ${cfg.user}:${cfg.group} "${cfg.stateFile}.tmp" + chown ${cfg.user}:${cfg.group} "${cfg.ledisDir}" fi - ''; + ''; serviceConfig = { PermissionsStartOnly = true; diff --git a/nixos/modules/services/monitoring/collectd.nix b/nixos/modules/services/monitoring/collectd.nix index 717c2c481683..3c3d83c66ed0 100644 --- a/nixos/modules/services/monitoring/collectd.nix +++ b/nixos/modules/services/monitoring/collectd.nix @@ -34,6 +34,15 @@ in { type = bool; }; + package = mkOption { + default = pkgs.collectd; + defaultText = "pkgs.collectd"; + description = '' + Which collectd package to use. + ''; + type = package; + }; + user = mkOption { default = "collectd"; description = '' @@ -91,7 +100,7 @@ in { wantedBy = [ "multi-user.target" ]; serviceConfig = { - ExecStart = "${pkgs.collectd}/sbin/collectd -C ${conf} -P ${cfg.pidFile}"; + ExecStart = "${cfg.package}/sbin/collectd -C ${conf} -P ${cfg.pidFile}"; Type = "forking"; PIDFile = cfg.pidFile; User = optional (cfg.user!="root") cfg.user; diff --git a/nixos/modules/services/monitoring/dd-agent.nix b/nixos/modules/services/monitoring/dd-agent.nix index 8c847af3bfc0..bd8d9950f77c 100644 --- a/nixos/modules/services/monitoring/dd-agent.nix +++ b/nixos/modules/services/monitoring/dd-agent.nix @@ -183,7 +183,6 @@ in { Restart = "always"; RestartSec = 2; }; - environment.SSL_CERT_FILE = "/etc/ssl/certs/ca-bundle.crt"; restartTriggers = [ pkgs.dd-agent ddConf diskConfig networkConfig postgresqlConfig nginxConfig mongoConfig ]; }; diff --git a/nixos/modules/services/monitoring/grafana.nix b/nixos/modules/services/monitoring/grafana.nix index 0b49038dd273..5c6f063b1493 100644 --- a/nixos/modules/services/monitoring/grafana.nix +++ b/nixos/modules/services/monitoring/grafana.nix @@ -87,6 +87,7 @@ in { staticRootPath = mkOption { description = "Root path for static assets."; + default = "${cfg.package}/share/grafana/public"; type = types.str; }; @@ -232,8 +233,5 @@ in { home = cfg.dataDir; createHome = true; }; - - services.grafana.staticRootPath = mkDefault "${cfg.package}/share/grafana/public"; - }; } diff --git a/nixos/modules/services/monitoring/graphite.nix b/nixos/modules/services/monitoring/graphite.nix index 976fd253a7cd..e59be1ea6c31 100644 --- a/nixos/modules/services/monitoring/graphite.nix +++ b/nixos/modules/services/monitoring/graphite.nix @@ -490,7 +490,7 @@ in { "${cfg.api.package}/lib/python2.7/site-packages:" + concatMapStringsSep ":" (f: f + "/lib/python2.7/site-packages") cfg.api.finders; GRAPHITE_API_CONFIG = graphiteApiConfig; - LD_LIBRARY_PATH = "${pkgs.cairo}/lib"; + LD_LIBRARY_PATH = "${pkgs.cairo.out}/lib"; }; serviceConfig = { ExecStart = '' diff --git a/nixos/modules/services/monitoring/hdaps.nix b/nixos/modules/services/monitoring/hdaps.nix new file mode 100644 index 000000000000..be26c44e78d1 --- /dev/null +++ b/nixos/modules/services/monitoring/hdaps.nix @@ -0,0 +1,22 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.hdapsd; + hdapsd = [ pkgs.hdapsd ]; +in +{ + options = { + services.hdapsd.enable = mkEnableOption + '' + Hard Drive Active Protection System Daemon, + devices are detected and managed automatically by udev and systemd + ''; + }; + + config = mkIf cfg.enable { + services.udev.packages = hdapsd; + systemd.packages = hdapsd; + }; +} diff --git a/nixos/modules/services/network-filesystems/netatalk.nix b/nixos/modules/services/network-filesystems/netatalk.nix new file mode 100644 index 000000000000..bff54406a2b0 --- /dev/null +++ b/nixos/modules/services/network-filesystems/netatalk.nix @@ -0,0 +1,150 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let + + cfg = config.services.netatalk; + + extmapFile = pkgs.writeText "extmap.conf" cfg.extmap; + + afpToString = x: if builtins.typeOf x == "bool" + then (if x then "true" else "false") + else toString x; + + volumeConfig = name: + let vol = getAttr name cfg.volumes; in + "[${name}]\n " + (toString ( + map + (key: "${key} = ${afpToString (getAttr key vol)}\n") + (attrNames vol) + )); + + afpConf = ''[Global] + extmap file = ${extmapFile} + afp port = ${toString cfg.port} + + ${cfg.extraConfig} + + ${if cfg.homes.enable then ''[Homes] + ${optionalString (cfg.homes.path != "") "path = ${cfg.homes.path}"} + basedir regex = ${cfg.homes.basedirRegex} + ${cfg.homes.extraConfig} + '' else ""} + + ${toString (map volumeConfig (attrNames cfg.volumes))} + ''; + + afpConfFile = pkgs.writeText "afp.conf" afpConf; + +in + +{ + options = { + services.netatalk = { + + enable = mkOption { + default = false; + description = "Whether to enable the Netatalk AFP fileserver."; + }; + + port = mkOption { + default = 548; + description = "TCP port to be used for AFP."; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + example = "uam list = uams_guest.so"; + description = '' + Lines of configuration to add to the <literal>[Global]</literal> section. + See <literal>man apf.conf</literal> for more information. + ''; + }; + + homes = { + enable = mkOption { + default = false; + description = "Enable sharing of the UNIX server user home directories."; + }; + + path = mkOption { + default = ""; + example = "afp-data"; + description = "Share not the whole user home but this subdirectory path."; + }; + + basedirRegex = mkOption { + example = "/home"; + description = "Regex which matches the parent directory of the user homes."; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + description = '' + Lines of configuration to add to the <literal>[Homes]</literal> section. + See <literal>man apf.conf</literal> for more information. + ''; + }; + }; + + volumes = mkOption { + default = { }; + type = types.attrsOf (types.attrsOf types.unspecified); + description = + '' + Set of AFP volumes to export. + See <literal>man apf.conf</literal> for more information. + ''; + example = + { srv = + { path = "/srv"; + "read only" = true; + "hosts allow" = "10.1.0.0/16 10.2.1.100 2001:0db8:1234::/48"; + }; + }; + }; + + extmap = mkOption { + type = types.lines; + default = ""; + description = '' + File name extension mappings. + See <literal>man extmap.conf</literal> for more information. + ''; + }; + + }; + }; + + config = mkIf cfg.enable { + + systemd.services.netatalk = { + description = "Netatalk AFP fileserver for Macintosh clients"; + unitConfig.Documentation = "man:afp.conf(5) man:netatalk(8) man:afpd(8) man:cnid_metad(8) man:cnid_dbd(8)"; + after = [ "network.target" "avahi-daemon.service" ]; + wantedBy = [ "multi-user.target" ]; + + path = [ pkgs.netatalk ]; + + serviceConfig = { + Type = "forking"; + GuessMainPID = "no"; + PIDFile = "/run/lock/netatalk"; + ExecStartPre = "${pkgs.coreutils}/bin/mkdir -m 0755 -p /var/lib/netatalk/CNID"; + ExecStart = "${pkgs.netatalk}/sbin/netatalk -F ${afpConfFile}"; + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + ExecStop = "${pkgs.coreutils}/bin/kill -TERM $MAINPID"; + Restart = "always"; + RestartSec = 1; + }; + + }; + + security.pam.services.netatalk.unixAuth = true; + + }; + +} diff --git a/nixos/modules/services/network-filesystems/samba.nix b/nixos/modules/services/network-filesystems/samba.nix index 576e5c9e87a3..a186982ec9c0 100644 --- a/nixos/modules/services/network-filesystems/samba.nix +++ b/nixos/modules/services/network-filesystems/samba.nix @@ -79,6 +79,14 @@ in description = '' Whether to enable Samba, which provides file and print services to Windows clients through the SMB/CIFS protocol. + + <note> + <para>If you use the firewall consider adding the following:</para> + <programlisting> + networking.firewall.allowedTCPPorts = [ 139 445 ]; + networking.firewall.allowedUDPPorts = [ 137 138 ]; + </programlisting> + </note> ''; }; @@ -86,7 +94,7 @@ in type = types.package; default = pkgs.samba; defaultText = "pkgs.samba"; - example = literalExample "pkgs.samba4"; + example = literalExample "pkgs.samba3"; description = '' Defines which package should be used for the samba server. ''; @@ -118,6 +126,10 @@ in description = '' Additional global section and extra section lines go in here. ''; + example = '' + guest account = nobody + map to guest = bad user + ''; }; configText = mkOption { @@ -154,9 +166,11 @@ in ''; type = types.attrsOf (types.attrsOf types.unspecified); example = - { srv = - { path = "/srv"; + { public = + { path = "/srv/public"; "read only" = true; + browseable = "yes"; + "guest ok" = "yes"; comment = "Public samba share."; }; }; diff --git a/nixos/modules/services/networking/bird.nix b/nixos/modules/services/networking/bird.nix index e7e1db191529..e76cdac14ca8 100644 --- a/nixos/modules/services/networking/bird.nix +++ b/nixos/modules/services/networking/bird.nix @@ -30,7 +30,7 @@ in user = mkOption { type = types.string; - default = "ircd"; + default = "bird"; description = '' BIRD Internet Routing Daemon user. ''; @@ -38,7 +38,7 @@ in group = mkOption { type = types.string; - default = "ircd"; + default = "bird"; description = '' BIRD Internet Routing Daemon group. ''; diff --git a/nixos/modules/services/networking/connman.nix b/nixos/modules/services/networking/connman.nix index deb1cbfc1858..3fecfbb13a04 100644 --- a/nixos/modules/services/networking/connman.nix +++ b/nixos/modules/services/networking/connman.nix @@ -53,13 +53,13 @@ in { config = mkIf cfg.enable { assertions = [{ - assertion = config.networking.useDHCP == false; + assertion = !config.networking.useDHCP; message = "You can not use services.networking.connman with services.networking.useDHCP"; }{ - assertion = config.networking.wireless.enable == true; + assertion = config.networking.wireless.enable; message = "You must use services.networking.connman with services.networking.wireless"; }{ - assertion = config.networking.networkmanager.enable == false; + assertion = !config.networking.networkmanager.enable; message = "You can not use services.networking.connman with services.networking.networkmanager"; }]; diff --git a/nixos/modules/services/networking/consul.nix b/nixos/modules/services/networking/consul.nix index 58dad56014b0..2aa101f980da 100644 --- a/nixos/modules/services/networking/consul.nix +++ b/nixos/modules/services/networking/consul.nix @@ -33,6 +33,7 @@ in package = mkOption { type = types.package; default = pkgs.consul; + defaultText = "pkgs.consul"; description = '' The package used for the Consul agent and CLI. ''; diff --git a/nixos/modules/services/networking/ddclient.nix b/nixos/modules/services/networking/ddclient.nix index ee06dfbbca3a..c5dd1e71c189 100644 --- a/nixos/modules/services/networking/ddclient.nix +++ b/nixos/modules/services/networking/ddclient.nix @@ -127,7 +127,6 @@ in wantedBy = [ "multi-user.target" ]; after = [ "network.target" ]; - environment.SSL_CERT_FILE = "/etc/ssl/certs/ca-bundle.crt"; serviceConfig = { # Uncomment this if too many problems occur: # Type = "forking"; diff --git a/nixos/modules/services/networking/dnscrypt-proxy.nix b/nixos/modules/services/networking/dnscrypt-proxy.nix index 9340be28205a..016b6a12cd61 100644 --- a/nixos/modules/services/networking/dnscrypt-proxy.nix +++ b/nixos/modules/services/networking/dnscrypt-proxy.nix @@ -52,7 +52,10 @@ in default = "opendns"; type = types.nullOr types.string; description = '' - The name of the upstream DNSCrypt resolver to use. + The name of the upstream DNSCrypt resolver to use. See + <literal>${resolverListFile}</literal> for alternative resolvers + (e.g., if you are concerned about logging and/or server + location). ''; }; customResolver = mkOption { diff --git a/nixos/modules/services/networking/ejabberd.nix b/nixos/modules/services/networking/ejabberd.nix index 7af11f37a43c..8ffce23a4b10 100644 --- a/nixos/modules/services/networking/ejabberd.nix +++ b/nixos/modules/services/networking/ejabberd.nix @@ -32,6 +32,7 @@ in { package = mkOption { type = types.package; default = pkgs.ejabberd; + defaultText = "pkgs.ejabberd"; description = "ejabberd server package to use"; }; diff --git a/nixos/modules/services/networking/gale.nix b/nixos/modules/services/networking/gale.nix index 3a5d9bd63c7b..bc975159cdfd 100644 --- a/nixos/modules/services/networking/gale.nix +++ b/nixos/modules/services/networking/gale.nix @@ -76,7 +76,7 @@ in system.activationScripts.gale = mkIf cfg.enable ( stringAfter [ "users" "groups" ] '' - chmod -R 755 ${home} + chmod 755 ${home} mkdir -m 0777 -p ${home}/auth/cache mkdir -m 1777 -p ${home}/auth/local # GALE_DOMAIN.gpub mkdir -m 0700 -p ${home}/auth/private # ROOT.gpub @@ -86,7 +86,8 @@ in mkdir -m 0700 -p ${home}/.gale/auth/private # GALE_DOMAIN.gpri ln -sf ${pkgs.gale}/etc/gale/auth/trusted/ROOT "${home}/auth/trusted/ROOT" - chown -R ${cfg.user}:${cfg.group} ${home} + chown ${cfg.user}:${cfg.group} ${home} ${home}/auth ${home}/auth/* + chown ${cfg.user}:${cfg.group} ${home}/.gale ${home}/.gale/auth ${home}/.gale/auth/private '' ); @@ -149,10 +150,9 @@ in after = [ "network.target" ]; preStart = '' - install -m 0640 ${keyPath}/${cfg.domain}.gpri "${home}/.gale/auth/private/" - install -m 0644 ${gpubFile} "${home}/.gale/auth/private/${cfg.domain}.gpub" - install -m 0644 ${gpubFile} "${home}/auth/local/${cfg.domain}.gpub" - chown -R ${cfg.user}:${cfg.group} ${home} + install -m 0640 -o ${cfg.user} -g ${cfg.group} ${keyPath}/${cfg.domain}.gpri "${home}/.gale/auth/private/" + install -m 0644 -o ${cfg.user} -g ${cfg.group} ${gpubFile} "${home}/.gale/auth/private/${cfg.domain}.gpub" + install -m 0644 -o ${cfg.user} -g ${cfg.group} ${gpubFile} "${home}/auth/local/${cfg.domain}.gpub" ''; serviceConfig = { diff --git a/nixos/modules/services/networking/gateone.nix b/nixos/modules/services/networking/gateone.nix index 93273837181e..78ff0b76198c 100644 --- a/nixos/modules/services/networking/gateone.nix +++ b/nixos/modules/services/networking/gateone.nix @@ -21,7 +21,7 @@ options = { }; config = mkIf cfg.enable { environment.systemPackages = with pkgs.pythonPackages; [ - gateone pkgs.openssh pkgs.procps pkgs.coreutils ]; + gateone pkgs.openssh pkgs.procps pkgs.coreutils pkgs.cacert]; users.extraUsers.gateone = { description = "GateOne privilege separation user"; @@ -49,8 +49,6 @@ config = mkIf cfg.enable { User = "gateone"; Group = "gateone"; WorkingDirectory = cfg.settingsDir; - PermissionsStartOnly = true; - }; wantedBy = [ "multi-user.target" ]; diff --git a/nixos/modules/services/networking/i2pd.nix b/nixos/modules/services/networking/i2pd.nix index c467402dbcf6..c32b935cf940 100644 --- a/nixos/modules/services/networking/i2pd.nix +++ b/nixos/modules/services/networking/i2pd.nix @@ -12,21 +12,69 @@ let toOneZero = b: if b then "1" else "0"; + mkEndpointOpt = name: addr: port: { + name = mkOption { + type = types.str; + default = name; + description = "The endpoint name."; + }; + address = mkOption { + type = types.str; + default = addr; + description = "Bind address for ${name} endpoint. Default: " + addr; + }; + port = mkOption { + type = types.int; + default = port; + description = "Bind port for ${name} endoint. Default: " + toString port; + }; + }; + + commonTunOpts = let + i2cpOpts = { + length = mkOption { + type = types.int; + description = "Guaranteed minimum hops."; + default = 3; + }; + quantity = mkOption { + type = types.int; + description = "Number of simultaneous tunnels."; + default = 5; + }; + }; + in name: { + outbound = i2cpOpts; + inbound = i2cpOpts; + crypto.tagsToSend = mkOption { + type = types.int; + description = "Number of ElGamal/AES tags to send."; + default = 40; + }; + destination = mkOption { + type = types.str; + description = "Remote endpoint, I2P hostname or b32.i2p address."; + }; + keys = mkOption { + type = types.str; + default = name + "-keys.dat"; + description = "Keyset used for tunnel identity."; + }; + } // mkEndpointOpt name "127.0.0.1" 0; + i2pdConf = pkgs.writeText "i2pd.conf" '' - v6 = ${toOneZero cfg.enableIPv6} - unreachable = ${toOneZero cfg.unreachable} + ipv6 = ${toOneZero cfg.enableIPv6} + notransit = ${toOneZero cfg.notransit} floodfill = ${toOneZero cfg.floodfill} ${if isNull cfg.port then "" else "port = ${toString cfg.port}"} - httpproxyport = ${toString cfg.proxy.httpPort} - socksproxyport = ${toString cfg.proxy.socksPort} - ircaddress = ${cfg.irc.host} - ircport = ${toString cfg.irc.port} - ircdest = ${cfg.irc.dest} - irckeys = ${cfg.irc.keyFile} - eepport = ${toString cfg.eep.port} - ${if isNull cfg.sam.port then "" else "--samport=${toString cfg.sam.port}"} - eephost = ${cfg.eep.host} - eepkeys = ${cfg.eep.keyFile} + ${flip concatMapStrings + (collect (proto: proto ? port && proto ? address && proto ? name) cfg.proto) + (proto: let portStr = toString proto.port; in '' + [${proto.name}] + address = ${proto.address} + port = ${toString proto.port} + '') + } ''; i2pdTunnelConf = pkgs.writeText "i2pd-tunnels.conf" '' @@ -39,10 +87,15 @@ let keys = ${tun.keys} address = ${tun.address} port = ${toString tun.port} + inbound.length = ${toString tun.inbound.length} + outbound.length = ${toString tun.outbound.length} + inbound.quantity = ${toString tun.inbound.quantity} + outbound.quantity = ${toString tun.outbound.quantity} + crypto.tagsToSend = ${toString tun.crypto.tagsToSend} '') } ${flip concatMapStrings - (collect (tun: tun ? port && tun ? host) cfg.outTunnels) + (collect (tun: tun ? port && tun ? host) cfg.inTunnels) (tun: let portStr = toString tun.port; in '' [${tun.name}] type = server @@ -59,10 +112,10 @@ let i2pdSh = pkgs.writeScriptBin "i2pd" '' #!/bin/sh ${if isNull cfg.extIp then extip else ""} - ${pkgs.i2pd}/bin/i2pd --log=1 --daemon=0 --service=0 \ + ${pkgs.i2pd}/bin/i2pd --log=1 \ --host=${if isNull cfg.extIp then "$EXTIP" else cfg.extIp} \ --conf=${i2pdConf} \ - --tunnelscfg=${i2pdTunnelConf} + --tunconf=${i2pdTunnelConf} ''; in @@ -91,11 +144,11 @@ in ''; }; - unreachable = mkOption { + notransit = mkOption { type = types.bool; default = false; description = '' - If the router is declared to be unreachable and needs introduction nodes. + Tells the router to not accept transit tunnels during startup. ''; }; @@ -111,7 +164,7 @@ in type = with types; nullOr int; default = null; description = '' - I2P listen port. If no one is given the router will pick between 9111 and 30777. + I2P listen port. If no one is given the router will pick between 9111 and 30777. ''; }; @@ -123,184 +176,53 @@ in ''; }; - http = { - port = mkOption { - type = types.int; - default = 7070; - description = '' - HTTP listen port. - ''; - }; - }; - - proxy = { - httpPort = mkOption { - type = types.int; - default = 4446; - description = '' - HTTP proxy listen port. - ''; - }; - socksPort = mkOption { - type = types.int; - default = 4447; - description = '' - SOCKS proxy listen port. - ''; - }; - }; - - irc = { - host = mkOption { - type = types.str; - default = "127.0.0.1"; - description = '' - Address to forward incoming traffic to. 127.0.0.1 by default. - ''; - }; - dest = mkOption { - type = types.str; - default = "irc.postman.i2p"; - description = '' - Destination I2P tunnel endpoint address of IRC server. irc.postman.i2p by default. - ''; - }; - port = mkOption { - type = types.int; - default = 6668; - description = '' - Local IRC tunnel endoint port to listen on. 6668 by default. - ''; - }; - keyFile = mkOption { - type = types.str; - default = "privKeys.dat"; - description = '' - File name containing destination keys. privKeys.dat by default. - ''; - }; - }; - - eep = { - host = mkOption { - type = types.str; - default = "127.0.0.1"; - description = '' - Address to forward incoming traffic to. 127.0.0.1 by default. - ''; - }; - port = mkOption { - type = types.int; - default = 80; - description = '' - Port to forward incoming traffic to. 80 by default. - ''; - }; - keyFile = mkOption { - type = types.str; - default = "privKeys.dat"; - description = '' - File name containing destination keys. privKeys.dat by default. - ''; - }; - }; - - sam = { - port = mkOption { - type = with types; nullOr int; - default = null; - description = '' - Local SAM tunnel endpoint. Usually 7656. SAM is disabled if not specified. - ''; - }; - }; + proto.http = mkEndpointOpt "http" "127.0.0.1" 7070; + proto.sam = mkEndpointOpt "sam" "127.0.0.1" 7656; + proto.bob = mkEndpointOpt "bob" "127.0.0.1" 2827; + proto.i2pControl = mkEndpointOpt "i2pcontrol" "127.0.0.1" 7650; + proto.httpProxy = mkEndpointOpt "httpproxy" "127.0.0.1" 4446; + proto.socksProxy = mkEndpointOpt "socksproxy" "127.0.0.1" 4447; outTunnels = mkOption { default = {}; - type = with types; loaOf optionSet; - description = '' - ''; - options = [ ({ name, config, ... }: { - - options = { - name = mkOption { - type = types.str; - description = "The name of the tunnel."; - }; - destination = mkOption { - type = types.str; - description = "Remote endpoint, I2P hostname or b32.i2p address."; - }; - keys = mkOption { - type = types.str; - default = name + "-keys.dat"; - description = "Keyset used for tunnel identity."; - }; - address = mkOption { - type = types.str; - default = "127.0.0.1"; - description = "Local bind address for tunnel."; - }; - port = mkOption { - type = types.int; - default = 0; - description = "Local tunnel listen port."; - }; - }; - - config = { - name = mkDefault name; - }; - - }) ]; + type = with types; loaOf optionSet; + description = '' + Connect to someone as a client and establish a local accept endpoint + ''; + options = [ ({ name, config, ... }: { + options = commonTunOpts name; + config = { + name = mkDefault name; + }; + }) ]; }; inTunnels = mkOption { default = {}; - type = with types; loaOf optionSet; - description = '' - ''; - options = [ ({ name, config, ... }: { - - options = { - - name = mkOption { - type = types.str; - description = "The name of the tunnel."; - }; - keys = mkOption { - type = types.path; - default = name + "-keys.dat"; - description = "Keyset used for tunnel identity."; - }; - address = mkOption { - type = types.str; - default = "127.0.0.1"; - description = "Local service IP address."; - }; - port = mkOption { - type = types.int; - default = 0; - description = "Local tunnel listen port."; - }; - inPort = mkOption { - type = types.int; - default = 0; - description = "I2P service port. Default to the tunnel's listen port."; - }; - accessList = mkOption { - type = with types; listOf str; - default = []; - description = "I2P nodes that are allowed to connect to this service."; - }; - - }; - - config = { - name = mkDefault name; - }; - - }) ]; + type = with types; loaOf optionSet; + description = '' + Serve something on I2P network at port and delegate requests to address inPort. + ''; + options = [ ({ name, config, ... }: { + + options = { + inPort = mkOption { + type = types.int; + default = 0; + description = "Service port. Default to the tunnel's listen port."; + }; + accessList = mkOption { + type = with types; listOf str; + default = []; + description = "I2P nodes that are allowed to connect to this service."; + }; + } // commonTunOpts name; + + config = { + name = mkDefault name; + }; + + }) ]; }; }; }; diff --git a/nixos/modules/services/networking/ifplugd.nix b/nixos/modules/services/networking/ifplugd.nix deleted file mode 100644 index 00b94fe2284e..000000000000 --- a/nixos/modules/services/networking/ifplugd.nix +++ /dev/null @@ -1,82 +0,0 @@ -{ config, lib, pkgs, ... }: - -with lib; - -let - - inherit (pkgs) ifplugd; - - cfg = config.networking.interfaceMonitor; - - # The ifplugd action script, which is called whenever the link - # status changes (i.e., a cable is plugged in or unplugged). - plugScript = pkgs.writeScript "ifplugd.action" - '' - #! ${pkgs.stdenv.shell} - iface="$1" - status="$2" - ${cfg.commands} - ''; - -in - -{ - - ###### interface - - options = { - - networking.interfaceMonitor.enable = mkOption { - type = types.bool; - default = false; - description = '' - If <literal>true</literal>, monitor Ethernet interfaces for - cables being plugged in or unplugged. When this occurs, the - commands specified in - <option>networking.interfaceMonitor.commands</option> are - executed. - ''; - }; - - networking.interfaceMonitor.beep = mkOption { - type = types.bool; - default = false; - description = '' - If <literal>true</literal>, beep when an Ethernet cable is - plugged in or unplugged. - ''; - }; - - networking.interfaceMonitor.commands = mkOption { - type = types.lines; - default = ""; - description = '' - Shell commands to be executed when the link status of an - interface changes. On invocation, the shell variable - <varname>iface</varname> contains the name of the interface, - while the variable <varname>status</varname> contains either - <literal>up</literal> or <literal>down</literal> to indicate - the new status. - ''; - }; - - }; - - - ###### implementation - - config = mkIf cfg.enable { - systemd.services.ifplugd = { - description = "Network interface connectivity monitor"; - after = [ "network-interfaces.target" ]; - wantedBy = [ "multi-user.target" ]; - script = '' - ${ifplugd}/sbin/ifplugd --no-daemon --no-startup --no-shutdown \ - ${if config.networking.interfaceMonitor.beep then "" else "--no-beep"} \ - --run ${plugScript} - ''; - }; - - environment.systemPackages = [ ifplugd ]; - }; -} diff --git a/nixos/modules/services/networking/kippo.nix b/nixos/modules/services/networking/kippo.nix index 7d70a3d05fa7..5f3efcd133a1 100644 --- a/nixos/modules/services/networking/kippo.nix +++ b/nixos/modules/services/networking/kippo.nix @@ -54,7 +54,7 @@ rec { }; config = mkIf cfg.enable { environment.systemPackages = with pkgs.pythonPackages; [ - python twisted pycrypto pyasn1 ]; + python twisted_11 pycrypto pyasn1 ]; environment.etc."kippo.cfg".text = '' # Automatically generated by NixOS. @@ -84,7 +84,7 @@ rec { description = "Kippo Web Server"; after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; - environment.PYTHONPATH = "${pkgs.kippo}/src/:${pkgs.pythonPackages.pycrypto}/lib/python2.7/site-packages/:${pkgs.pythonPackages.pyasn1}/lib/python2.7/site-packages/:${pkgs.pythonPackages.python}/lib/python2.7/site-packages/:${pkgs.pythonPackages.twisted}/lib/python2.7/site-packages/:."; + environment.PYTHONPATH = "${pkgs.kippo}/src/:${pkgs.pythonPackages.pycrypto}/lib/python2.7/site-packages/:${pkgs.pythonPackages.pyasn1}/lib/python2.7/site-packages/:${pkgs.pythonPackages.python}/lib/python2.7/site-packages/:${pkgs.pythonPackages.twisted_11}/lib/python2.7/site-packages/:."; preStart = '' if [ ! -d ${cfg.varPath}/ ] ; then mkdir -p ${cfg.logPath}/tty @@ -107,7 +107,7 @@ rec { fi ''; - serviceConfig.ExecStart = "${pkgs.pythonPackages.twisted}/bin/twistd -y ${pkgs.kippo}/src/kippo.tac --syslog --rundir=${cfg.varPath}/ --pidfile=${cfg.pidPath}/kippo.pid --prefix=kippo -n"; + serviceConfig.ExecStart = "${pkgs.pythonPackages.twisted_11}/bin/twistd -y ${pkgs.kippo}/src/kippo.tac --syslog --rundir=${cfg.varPath}/ --pidfile=${cfg.pidPath}/kippo.pid --prefix=kippo -n"; serviceConfig.PermissionsStartOnly = true; serviceConfig.User = "kippo"; serviceConfig.Group = "kippo"; diff --git a/nixos/modules/services/networking/libreswan.nix b/nixos/modules/services/networking/libreswan.nix new file mode 100644 index 000000000000..3866b216f8ef --- /dev/null +++ b/nixos/modules/services/networking/libreswan.nix @@ -0,0 +1,126 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.libreswan; + + libexec = "${pkgs.libreswan}/libexec/ipsec"; + ipsec = "${pkgs.libreswan}/sbin/ipsec"; + + trim = chars: str: let + nonchars = filter (x : !(elem x.value chars)) + (imap (i: v: {ind = (sub i 1); value = v;}) (stringToCharacters str)); + in + if length nonchars == 0 then "" + else substring (head nonchars).ind (add 1 (sub (last nonchars).ind (head nonchars).ind)) str; + indent = str: concatStrings (concatMap (s: [" " (trim [" " "\t"] s) "\n"]) (splitString "\n" str)); + configText = indent (toString cfg.configSetup); + connectionText = concatStrings (mapAttrsToList (n: v: + '' + conn ${n} + ${indent v} + + '') cfg.connections); + configFile = pkgs.writeText "ipsec.conf" + '' + config setup + ${configText} + + ${connectionText} + ''; + +in + +{ + + ###### interface + + options = { + + services.libreswan = { + + enable = mkEnableOption "libreswan ipsec service"; + + configSetup = mkOption { + type = types.lines; + default = '' + protostack=netkey + nat_traversal=yes + virtual_private=%v4:10.0.0.0/8,%v4:192.168.0.0/16,%v4:172.16.0.0/12,%v4:25.0.0.0/8,%v4:100.64.0.0/10,%v6:fd00::/8,%v6:fe80::/10 + ''; + example = '' + secretsfile=/root/ipsec.secrets + protostack=netkey + nat_traversal=yes + virtual_private=%v4:10.0.0.0/8,%v4:192.168.0.0/16,%v4:172.16.0.0/12,%v4:25.0.0.0/8,%v4:100.64.0.0/10,%v6:fd00::/8,%v6:fe80::/10 + ''; + description = "Options to go in the 'config setup' section of the libreswan ipsec configuration"; + }; + + connections = mkOption { + type = types.attrsOf types.lines; + default = {}; + example = { + myconnection = '' + auto=add + left=%defaultroute + leftid=@user + + right=my.vpn.com + + ikev2=no + ikelifetime=8h + ''; + }; + description = "A set of connections to define for the libreswan ipsec service"; + }; + }; + + }; + + + ###### implementation + + config = mkIf cfg.enable { + + environment.systemPackages = [ pkgs.libreswan pkgs.iproute ]; + + systemd.services.ipsec = { + description = "Internet Key Exchange (IKE) Protocol Daemon for IPsec"; + path = [ + "${pkgs.libreswan}" + "${pkgs.iproute}" + "${pkgs.procps}" + ]; + + wants = [ "network-online.target" ]; + after = [ "network-online.target" ]; + wantedBy = [ "multi-user.target" ]; + + serviceConfig = { + Type = "simple"; + Restart = "always"; + EnvironmentFile = "${pkgs.libreswan}/etc/sysconfig/pluto"; + ExecStartPre = [ + "${libexec}/addconn --config ${configFile} --checkconfig" + "${libexec}/_stackmanager start" + "${ipsec} --checknss" + "${ipsec} --checknflog" + ]; + ExecStart = "${libexec}/pluto --config ${configFile} --nofork \$PLUTO_OPTIONS"; + ExecStop = "${libexec}/whack --shutdown"; + ExecStopPost = [ + "${pkgs.iproute}/bin/ip xfrm policy flush" + "${pkgs.iproute}/bin/ip xfrm state flush" + "${ipsec} --stopnflog" + ]; + ExecReload = "${libexec}/whack --listen"; + }; + + }; + + }; + +} diff --git a/nixos/modules/services/networking/networkmanager.nix b/nixos/modules/services/networking/networkmanager.nix index 01c05fb4a245..9912ad9ae3fc 100644 --- a/nixos/modules/services/networking/networkmanager.nix +++ b/nixos/modules/services/networking/networkmanager.nix @@ -21,6 +21,9 @@ let [logging] level=WARN + + [connection] + ipv6.ip6-privacy=2 ''; /* @@ -228,6 +231,11 @@ in { users.extraUsers = [{ name = "nm-openvpn"; uid = config.ids.uids.nm-openvpn; + } + { + # to enable link-local connections + name = "avahi-autoipd"; + uid = config.ids.uids.avahi-autoipd; }]; systemd.packages = cfg.packages; diff --git a/nixos/modules/services/networking/nix-serve.nix b/nixos/modules/services/networking/nix-serve.nix index 880a1d361dfe..8f6881441cf7 100644 --- a/nixos/modules/services/networking/nix-serve.nix +++ b/nixos/modules/services/networking/nix-serve.nix @@ -50,7 +50,7 @@ in after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; - path = [ config.nix.package pkgs.bzip2 ]; + path = [ config.nix.package pkgs.bzip2.bin ]; environment.NIX_REMOTE = "daemon"; environment.NIX_SECRET_KEY_FILE = cfg.secretKeyFile; diff --git a/nixos/modules/services/networking/nntp-proxy.nix b/nixos/modules/services/networking/nntp-proxy.nix new file mode 100644 index 000000000000..cfa662c7311b --- /dev/null +++ b/nixos/modules/services/networking/nntp-proxy.nix @@ -0,0 +1,235 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + inherit (pkgs) nntp-proxy; + + proxyUser = "nntp-proxy"; + + cfg = config.services.nntp-proxy; + + configBool = b: if b then "TRUE" else "FALSE"; + + confFile = pkgs.writeText "nntp-proxy.conf" '' + nntp_server: + { + # NNTP Server host and port address + server = "${cfg.upstreamServer}"; + port = ${toString cfg.upstreamPort}; + # NNTP username + username = "${cfg.upstreamUser}"; + # NNTP password in clear text + password = "${cfg.upstreamPassword}"; + # Maximum number of connections allowed by the NNTP + max_connections = ${toString cfg.upstreamMaxConnections}; + }; + + proxy: + { + # Local address and port to bind to + bind_ip = "${cfg.listenAddress}"; + bind_port = ${toString cfg.port}; + + # SSL key and cert file + ssl_key = "${cfg.sslKey}"; + ssl_cert = "${cfg.sslCert}"; + + # prohibit users from posting + prohibit_posting = ${configBool cfg.prohibitPosting}; + # Verbose levels: ERROR, WARNING, NOTICE, INFO, DEBUG + verbose = "${toUpper cfg.verbosity}"; + # Password is made with: 'mkpasswd -m sha-512 <password>' + users = (${concatStringsSep ",\n" (mapAttrsToList (username: userConfig: + '' + { + username = "${username}"; + password = "${userConfig.passwordHash}"; + max_connections = ${toString userConfig.maxConnections}; + } + '') cfg.users)}); + }; + ''; + +in + +{ + + ###### interface + + options = { + + services.nntp-proxy = { + enable = mkEnableOption "NNTP-Proxy"; + + upstreamServer = mkOption { + type = types.str; + default = ""; + example = "ssl-eu.astraweb.com"; + description = '' + Upstream server address + ''; + }; + + upstreamPort = mkOption { + type = types.int; + default = 563; + description = '' + Upstream server port + ''; + }; + + upstreamMaxConnections = mkOption { + type = types.int; + default = 20; + description = '' + Upstream server maximum allowed concurrent connections + ''; + }; + + upstreamUser = mkOption { + type = types.str; + default = ""; + description = '' + Upstream server username + ''; + }; + + upstreamPassword = mkOption { + type = types.str; + default = ""; + description = '' + Upstream server password + ''; + }; + + listenAddress = mkOption { + type = types.str; + default = "127.0.0.1"; + example = "[::]"; + description = '' + Proxy listen address (IPv6 literal addresses need to be enclosed in "[" and "]" characters) + ''; + }; + + port = mkOption { + type = types.int; + default = 5555; + description = '' + Proxy listen port + ''; + }; + + sslKey = mkOption { + type = types.str; + default = "key.pem"; + example = "/path/to/your/key.file"; + description = '' + Proxy ssl key path + ''; + }; + + sslCert = mkOption { + type = types.str; + default = "cert.pem"; + example = "/path/to/your/cert.file"; + description = '' + Proxy ssl certificate path + ''; + }; + + prohibitPosting = mkOption { + type = types.bool; + default = true; + description = '' + Whether to prohibit posting to the upstream server + ''; + }; + + verbosity = mkOption { + type = types.str; + default = "info"; + example = "error"; + description = '' + Verbosity level (error, warning, notice, info, debug) + ''; + }; + + users = mkOption { + type = types.attrsOf (types.submodule { + options = { + username = mkOption { + type = types.str; + default = null; + description = '' + Username + ''; + }; + + passwordHash = mkOption { + type = types.str; + default = null; + example = "$6$GtzE7FrpE$wwuVgFYU.TZH4Rz.Snjxk9XGua89IeVwPQ/fEUD8eujr40q5Y021yhn0aNcsQ2Ifw.BLclyzvzgegopgKcneL0"; + description = '' + SHA-512 password hash (can be generated by + <code>mkpasswd -m sha-512 <password></code>) + ''; + }; + + maxConnections = mkOption { + type = types.int; + default = 1; + description = '' + Maximum number of concurrent connections to the proxy for this user + ''; + }; + }; + }); + description = '' + NNTP-Proxy user configuration + ''; + + default = {}; + example = literalExample '' + "user1" = { + passwordHash = "$6$1l0t5Kn2Dk$appzivc./9l/kjq57eg5UCsBKlcfyCr0zNWYNerKoPsI1d7eAwiT0SVsOVx/CTgaBNT/u4fi2vN.iGlPfv1ek0"; + maxConnections = 5; + }; + "anotheruser" = { + passwordHash = "$6$6lwEsWB.TmsS$W7m1riUx4QrA8pKJz8hvff0dnF1NwtZXgdjmGqA1Dx2MDPj07tI9GNcb0SWlMglE.2/hBgynDdAd/XqqtRqVQ0"; + maxConnections = 7; + }; + ''; + }; + }; + + }; + + ###### implementation + + config = mkIf cfg.enable { + + users.extraUsers = singleton + { name = proxyUser; + uid = config.ids.uids.nntp-proxy; + description = "NNTP-Proxy daemon user"; + }; + + systemd.services.nntp-proxy = { + description = "NNTP proxy"; + after = [ "network.target" "nss-lookup.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { User="${proxyUser}"; }; + serviceConfig.ExecStart = "${nntp-proxy}/bin/nntp-proxy ${confFile}"; + preStart = '' + if [ ! \( -f ${cfg.sslCert} -a -f ${cfg.sslKey} \) ]; then + ${pkgs.openssl}/bin/openssl req -subj '/CN=AutoGeneratedCert/O=NixOS Service/C=US' \ + -new -newkey rsa:2048 -days 365 -nodes -x509 -keyout ${cfg.sslKey} -out ${cfg.sslCert}; + fi + ''; + }; + + }; + +} diff --git a/nixos/modules/services/networking/nsd.nix b/nixos/modules/services/networking/nsd.nix index e85f26811257..333a3378c4cc 100644 --- a/nixos/modules/services/networking/nsd.nix +++ b/nixos/modules/services/networking/nsd.nix @@ -7,92 +7,118 @@ let username = "nsd"; stateDir = "/var/lib/nsd"; - pidFile = stateDir + "/var/nsd.pid"; + pidFile = stateDir + "/var/nsd.pid"; + # build nsd with the options needed for the given config nsdPkg = pkgs.nsd.override { bind8Stats = cfg.bind8Stats; - ipv6 = cfg.ipv6; - ratelimit = cfg.ratelimit.enable; + ipv6 = cfg.ipv6; + ratelimit = cfg.ratelimit.enable; rootServer = cfg.rootServer; - zoneStats = length (collect (x: (x.zoneStats or null) != null) cfg.zones) > 0; + zoneStats = length (collect (x: (x.zoneStats or null) != null) cfg.zones) > 0; }; - zoneFiles = pkgs.stdenv.mkDerivation { - preferLocalBuild = true; + + nsdEnv = pkgs.buildEnv { name = "nsd-env"; - buildCommand = concatStringsSep "\n" - [ "mkdir -p $out" - (concatStrings (mapAttrsToList (zoneName: zoneOptions: '' - cat > "$out/${zoneName}" <<_EOF_ - ${zoneOptions.data} - _EOF_ - '') zoneConfigs)) - ]; + + paths = [ configFile ] + ++ mapAttrsToList (name: zone: writeZoneData name zone.data) zoneConfigs; + + postBuild = '' + echo "checking zone files" + cd $out/zones + + for zoneFile in *; do + ${nsdPkg}/sbin/nsd-checkzone "$zoneFile" "$zoneFile" || { + if grep -q \\\\\\$ "$zoneFile"; then + echo zone "$zoneFile" contains escaped dollar signes \\\$ + echo Escaping them is not needed any more. Please make shure \ + to unescape them where they prefix a variable name + fi + + exit 1 + } + done + + echo "checking configuration file" + ${nsdPkg}/sbin/nsd-checkconf $out/nsd.conf + ''; + }; + + writeZoneData = name: text: pkgs.writeTextFile { + inherit name text; + destination = "/zones/${name}"; }; - configFile = pkgs.writeText "nsd.conf" '' + + # options are ordered alphanumerically by the nixos option name + configFile = pkgs.writeTextDir "nsd.conf" '' server: - username: ${username} chroot: "${stateDir}" + username: ${username} # The directory for zonefile: files. The daemon chdirs here. zonesdir: "${stateDir}" # the list of dynamically added zones. - zonelistfile: "${stateDir}/var/zone.list" database: "${stateDir}/var/nsd.db" pidfile: "${pidFile}" xfrdfile: "${stateDir}/var/xfrd.state" xfrdir: "${stateDir}/tmp" + zonelistfile: "${stateDir}/var/zone.list" # interfaces ${forEach " ip-address: " cfg.interfaces} - server-count: ${toString cfg.serverCount} + hide-version: ${yesOrNo cfg.hideVersion} + identity: "${cfg.identity}" ip-transparent: ${yesOrNo cfg.ipTransparent} do-ip4: ${yesOrNo cfg.ipv4} + ipv4-edns-size: ${toString cfg.ipv4EDNSSize} do-ip6: ${yesOrNo cfg.ipv6} - port: ${toString cfg.port} - verbosity: ${toString cfg.verbosity} - hide-version: ${yesOrNo cfg.hideVersion} - identity: "${cfg.identity}" + ipv6-edns-size: ${toString cfg.ipv6EDNSSize} + log-time-ascii: ${yesOrNo cfg.logTimeAscii} ${maybeString "nsid: " cfg.nsid} + port: ${toString cfg.port} + reuseport: ${yesOrNo cfg.reuseport} + round-robin: ${yesOrNo cfg.roundRobin} + server-count: ${toString cfg.serverCount} + ${if cfg.statistics == null then "" else "statistics: ${toString cfg.statistics}"} tcp-count: ${toString cfg.tcpCount} tcp-query-count: ${toString cfg.tcpQueryCount} tcp-timeout: ${toString cfg.tcpTimeout} - ipv4-edns-size: ${toString cfg.ipv4EDNSSize} - ipv6-edns-size: ${toString cfg.ipv6EDNSSize} - ${if cfg.statistics == null then "" else "statistics: ${toString cfg.statistics}"} + verbosity: ${toString cfg.verbosity} + ${maybeString "version: " cfg.version} xfrd-reload-timeout: ${toString cfg.xfrdReloadTimeout} zonefiles-check: ${yesOrNo cfg.zonefilesCheck} - rrl-size: ${toString cfg.ratelimit.size} - rrl-ratelimit: ${toString cfg.ratelimit.ratelimit} - rrl-whitelist-ratelimit: ${toString cfg.ratelimit.whitelistRatelimit} - ${maybeString "rrl-slip: " cfg.ratelimit.slip} ${maybeString "rrl-ipv4-prefix-length: " cfg.ratelimit.ipv4PrefixLength} ${maybeString "rrl-ipv6-prefix-length: " cfg.ratelimit.ipv6PrefixLength} + rrl-ratelimit: ${toString cfg.ratelimit.ratelimit} + ${maybeString "rrl-slip: " cfg.ratelimit.slip} + rrl-size: ${toString cfg.ratelimit.size} + rrl-whitelist-ratelimit: ${toString cfg.ratelimit.whitelistRatelimit} ${keyConfigFile} remote-control: control-enable: ${yesOrNo cfg.remoteControl.enable} + control-key-file: "${cfg.remoteControl.controlKeyFile}" + control-cert-file: "${cfg.remoteControl.controlCertFile}" ${forEach " control-interface: " cfg.remoteControl.interfaces} - control-port: ${toString cfg.port} + control-port: ${toString cfg.remoteControl.port} server-key-file: "${cfg.remoteControl.serverKeyFile}" server-cert-file: "${cfg.remoteControl.serverCertFile}" - control-key-file: "${cfg.remoteControl.controlKeyFile}" - control-cert-file: "${cfg.remoteControl.controlCertFile}" - # zone files reside in "${zoneFiles}" linked to "${stateDir}/zones" ${concatStrings (mapAttrsToList zoneConfigFile zoneConfigs)} ${cfg.extraConfig} ''; - yesOrNo = b: if b then "yes" else "no"; + yesOrNo = b: if b then "yes" else "no"; maybeString = pre: s: if s == null then "" else ''${pre} "${s}"''; - forEach = pre: l: concatMapStrings (x: pre + x + "\n") l; + forEach = pre: l: concatMapStrings (x: pre + x + "\n") l; keyConfigFile = concatStrings (mapAttrsToList (keyName: keyOptions: '' @@ -106,22 +132,23 @@ let secret=$(cat "${keyOptions.keyFile}") dest="${stateDir}/private/${keyName}" echo " secret: \"$secret\"" > "$dest" - ${pkgs.coreutils}/bin/chown ${username}:${username} "$dest" - ${pkgs.coreutils}/bin/chmod 0400 "$dest" + chown ${username}:${username} "$dest" + chmod 0400 "$dest" '') cfg.keys); + # options are ordered alphanumerically by the nixos option name zoneConfigFile = name: zone: '' zone: name: "${name}" zonefile: "${stateDir}/zones/${name}" - ${maybeString "zonestats: " zone.zoneStats} ${maybeString "outgoing-interface: " zone.outgoingInterface} ${forEach " rrl-whitelist: " zone.rrlWhitelist} + ${maybeString "zonestats: " zone.zoneStats} + allow-axfr-fallback: ${yesOrNo zone.allowAXFRFallback} ${forEach " allow-notify: " zone.allowNotify} ${forEach " request-xfr: " zone.requestXFR} - allow-axfr-fallback: ${yesOrNo zone.allowAXFRFallback} ${forEach " notify: " zone.notify} notify-retry: ${toString zone.notifyRetry} @@ -142,7 +169,7 @@ let ); # fighting infinite recursion - zoneOptions = zoneOptionsRaw // childConfig zoneOptions1 true; + zoneOptions = zoneOptionsRaw // childConfig zoneOptions1 true; zoneOptions1 = zoneOptionsRaw // childConfig zoneOptions2 false; zoneOptions2 = zoneOptionsRaw // childConfig zoneOptions3 false; zoneOptions3 = zoneOptionsRaw // childConfig zoneOptions4 false; @@ -152,26 +179,25 @@ let childConfig = x: v: { options.children = { type = types.attrsOf x; visible = v; }; }; + # options are ordered alphanumerically zoneOptionsRaw = types.submodule { options = { - children = mkOption { - default = {}; + + allowAXFRFallback = mkOption { + type = types.bool; + default = true; description = '' - Children zones inherit all options of their parents. Attributes - defined in a child will overwrite the ones of its parent. Only - leaf zones will be actually served. This way it's possible to - define maybe zones which share most attributes without - duplicating everything. This mechanism replaces nsd's patterns - in a save and functional way. + If NSD as secondary server should be allowed to AXFR if the primary + server does not allow IXFR. ''; }; allowNotify = mkOption { - type = types.listOf types.str; - default = [ ]; - example = [ "192.0.2.0/24 NOKEY" "10.0.0.1-10.0.0.5 my_tsig_key_name" - "10.0.3.4&255.255.0.0 BLOCKED" - ]; + type = types.listOf types.str; + default = [ ]; + example = [ "192.0.2.0/24 NOKEY" "10.0.0.1-10.0.0.5 my_tsig_key_name" + "10.0.3.4&255.255.0.0 BLOCKED" + ]; description = '' Listed primary servers are allowed to notify this secondary server. <screen><![CDATA[ @@ -193,28 +219,32 @@ let ''; }; - requestXFR = mkOption { - type = types.listOf types.str; - default = []; - example = []; + children = mkOption { + default = {}; description = '' - Format: <code>[AXFR|UDP] <ip-address> <key-name | NOKEY></code> + Children zones inherit all options of their parents. Attributes + defined in a child will overwrite the ones of its parent. Only + leaf zones will be actually served. This way it's possible to + define maybe zones which share most attributes without + duplicating everything. This mechanism replaces nsd's patterns + in a save and functional way. ''; }; - allowAXFRFallback = mkOption { - type = types.bool; - default = true; + data = mkOption { + type = types.str; + default = ""; + example = ""; description = '' - If NSD as secondary server should be allowed to AXFR if the primary - server does not allow IXFR. + The actual zone data. This is the content of your zone file. + Use imports or pkgs.lib.readFile if you don't want this data in your config file. ''; }; notify = mkOption { - type = types.listOf types.str; - default = []; - example = [ "10.0.0.1@3721 my_key" "::5 NOKEY" ]; + type = types.listOf types.str; + default = []; + example = [ "10.0.0.1@3721 my_key" "::5 NOKEY" ]; description = '' This primary server will notify all given secondary servers about zone changes. @@ -231,38 +261,47 @@ let }; notifyRetry = mkOption { - type = types.int; - default = 5; + type = types.int; + default = 5; description = '' Specifies the number of retries for failed notifies. Set this along with notify. ''; }; + outgoingInterface = mkOption { + type = types.nullOr types.str; + default = null; + example = "2000::1@1234"; + description = '' + This address will be used for zone-transfere requests if configured + as a secondary server or notifications in case of a primary server. + Supply either a plain IPv4 or IPv6 address with an optional port + number (ip@port). + ''; + }; + provideXFR = mkOption { - type = types.listOf types.str; - default = []; - example = [ "192.0.2.0/24 NOKEY" "192.0.2.0/24 my_tsig_key_name" ]; + type = types.listOf types.str; + default = []; + example = [ "192.0.2.0/24 NOKEY" "192.0.2.0/24 my_tsig_key_name" ]; description = '' Allow these IPs and TSIG to transfer zones, addr TSIG|NOKEY|BLOCKED address range 192.0.2.0/24, 1.2.3.4&255.255.0.0, 3.0.2.20-3.0.2.40 ''; }; - outgoingInterface = mkOption { - type = types.nullOr types.str; - default = null; - example = "2000::1@1234"; + requestXFR = mkOption { + type = types.listOf types.str; + default = []; + example = []; description = '' - This address will be used for zone-transfere requests if configured - as a secondary server or notifications in case of a primary server. - Supply either a plain IPv4 or IPv6 address with an optional port - number (ip@port). + Format: <code>[AXFR|UDP] <ip-address> <key-name | NOKEY></code> ''; }; rrlWhitelist = mkOption { - type = types.listOf types.str; - default = []; + type = types.listOf types.str; + default = []; description = '' Whitelists the given rrl-types. The RRL classification types are: nxdomain, error, referral, any, @@ -270,20 +309,10 @@ let ''; }; - data = mkOption { - type = types.str; - default = ""; - example = ""; - description = '' - The actual zone data. This is the content of your zone file. - Use imports or pkgs.lib.readFile if you don't want this data in your config file. - ''; - }; - zoneStats = mkOption { - type = types.nullOr types.str; - default = null; - example = "%s"; + type = types.nullOr types.str; + default = null; + example = "%s"; description = '' When set to something distinct to null NSD is able to collect statistics per zone. All statistics of this zone(s) will be added @@ -292,419 +321,470 @@ let and stats_noreset. ''; }; + }; }; in { - options = { - services.nsd = { + # options are ordered alphanumerically + options.services.nsd = { - enable = mkEnableOption "NSD authoritative DNS server"; - bind8Stats = mkEnableOption "BIND8 like statistics"; + enable = mkEnableOption "NSD authoritative DNS server"; - rootServer = mkOption { - type = types.bool; - default = false; - description = '' - Wheter if this server will be a root server (a DNS root server, you - usually don't want that). - ''; - }; + bind8Stats = mkEnableOption "BIND8 like statistics"; - interfaces = mkOption { - type = types.listOf types.str; - default = [ "127.0.0.0" "::1" ]; - description = '' - What addresses the server should listen to. - ''; - }; + extraConfig = mkOption { + type = types.str; + default = ""; + description = '' + Extra nsd config. + ''; + }; - serverCount = mkOption { - type = types.int; - default = 1; - description = '' - Number of NSD servers to fork. Put the number of CPUs to use here. - ''; - }; + hideVersion = mkOption { + type = types.bool; + default = true; + description = '' + Whether NSD should answer VERSION.BIND and VERSION.SERVER CHAOS class queries. + ''; + }; - ipTransparent = mkOption { - type = types.bool; - default = false; - description = '' - Allow binding to non local addresses. - ''; - }; + identity = mkOption { + type = types.str; + default = "unidentified server"; + description = '' + Identify the server (CH TXT ID.SERVER entry). + ''; + }; - ipv4 = mkOption { - type = types.bool; - default = true; - description = '' - Wheter to listen on IPv4 connections. - ''; - }; + interfaces = mkOption { + type = types.listOf types.str; + default = [ "127.0.0.0" "::1" ]; + description = '' + What addresses the server should listen to. + ''; + }; - ipv6 = mkOption { - type = types.bool; - default = true; - description = '' - Wheter to listen on IPv6 connections. - ''; - }; + ipTransparent = mkOption { + type = types.bool; + default = false; + description = '' + Allow binding to non local addresses. + ''; + }; - port = mkOption { - type = types.int; - default = 53; - description = '' - Port the service should bind do. - ''; - }; + ipv4 = mkOption { + type = types.bool; + default = true; + description = '' + Whether to listen on IPv4 connections. + ''; + }; - verbosity = mkOption { - type = types.int; - default = 0; - description = '' - Verbosity level. - ''; - }; + ipv4EDNSSize = mkOption { + type = types.int; + default = 4096; + description = '' + Preferred EDNS buffer size for IPv4. + ''; + }; + + ipv6 = mkOption { + type = types.bool; + default = true; + description = '' + Whether to listen on IPv6 connections. + ''; + }; + + ipv6EDNSSize = mkOption { + type = types.int; + default = 4096; + description = '' + Preferred EDNS buffer size for IPv6. + ''; + }; + + logTimeAscii = mkOption { + type = types.bool; + default = true; + description = '' + Log time in ascii, if false then in unix epoch seconds. + ''; + }; + + nsid = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + NSID identity (hex string, or "ascii_somestring"). + ''; + }; + + port = mkOption { + type = types.int; + default = 53; + description = '' + Port the service should bind do. + ''; + }; + + reuseport = mkOption { + type = types.bool; + default = pkgs.stdenv.isLinux; + description = '' + Whether to enable SO_REUSEPORT on all used sockets. This lets multiple + processes bind to the same port. This speeds up operation especially + if the server count is greater than one and makes fast restarts less + prone to fail + ''; + }; + + rootServer = mkOption { + type = types.bool; + default = false; + description = '' + Whether this server will be a root server (a DNS root server, you + usually don't want that). + ''; + }; + + roundRobin = mkEnableOption "round robin rotation of records"; + + serverCount = mkOption { + type = types.int; + default = 1; + description = '' + Number of NSD servers to fork. Put the number of CPUs to use here. + ''; + }; + + statistics = mkOption { + type = types.nullOr types.int; + default = null; + description = '' + Statistics are produced every number of seconds. Prints to log. + If null no statistics are logged. + ''; + }; + + tcpCount = mkOption { + type = types.int; + default = 100; + description = '' + Maximum number of concurrent TCP connections per server. + ''; + }; + + tcpQueryCount = mkOption { + type = types.int; + default = 0; + description = '' + Maximum number of queries served on a single TCP connection. + 0 means no maximum. + ''; + }; + + tcpTimeout = mkOption { + type = types.int; + default = 120; + description = '' + TCP timeout in seconds. + ''; + }; + + verbosity = mkOption { + type = types.int; + default = 0; + description = '' + Verbosity level. + ''; + }; + + version = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + The version string replied for CH TXT version.server and version.bind + queries. Will use the compiled package version on null. + See hideVersion for enabling/disabling this responses. + ''; + }; + + xfrdReloadTimeout = mkOption { + type = types.int; + default = 1; + description = '' + Number of seconds between reloads triggered by xfrd. + ''; + }; + + zonefilesCheck = mkOption { + type = types.bool; + default = true; + description = '' + Whether to check mtime of all zone files on start and sighup. + ''; + }; + + + keys = mkOption { + type = types.attrsOf (types.submodule { + options = { + + algorithm = mkOption { + type = types.str; + default = "hmac-sha256"; + description = '' + Authentication algorithm for this key. + ''; + }; + + keyFile = mkOption { + type = types.path; + description = '' + Path to the file which contains the actual base64 encoded + key. The key will be copied into "${stateDir}/private" before + NSD starts. The copied file is only accessibly by the NSD + user. + ''; + }; + + }; + }); + default = {}; + example = literalExample '' + { "tsig.example.org" = { + algorithm = "hmac-md5"; + keyFile = "/path/to/my/key"; + }; + } + ''; + description = '' + Define your TSIG keys here. + ''; + }; + + + ratelimit = { + + enable = mkEnableOption "ratelimit capabilities"; - hideVersion = mkOption { - type = types.bool; - default = true; + ipv4PrefixLength = mkOption { + type = types.nullOr types.int; + default = null; description = '' - Wheter NSD should answer VERSION.BIND and VERSION.SERVER CHAOS class queries. + IPv4 prefix length. Addresses are grouped by netblock. ''; }; - identity = mkOption { - type = types.str; - default = "unidentified server"; + ipv6PrefixLength = mkOption { + type = types.nullOr types.int; + default = null; description = '' - Identify the server (CH TXT ID.SERVER entry). + IPv6 prefix length. Addresses are grouped by netblock. ''; }; - nsid = mkOption { - type = types.nullOr types.str; - default = null; + ratelimit = mkOption { + type = types.int; + default = 200; description = '' - NSID identity (hex string, or "ascii_somestring"). + Max qps allowed from any query source. + 0 means unlimited. With an verbosity of 2 blocked and + unblocked subnets will be logged. ''; }; - tcpCount = mkOption { - type = types.int; - default = 100; + slip = mkOption { + type = types.nullOr types.int; + default = null; description = '' - Maximum number of concurrent TCP connections per server. + Number of packets that get discarded before replying a SLIP response. + 0 disables SLIP responses. 1 will make every response a SLIP response. ''; }; - tcpQueryCount = mkOption { - type = types.int; - default = 0; + size = mkOption { + type = types.int; + default = 1000000; description = '' - Maximum number of queries served on a single TCP connection. - 0 means no maximum. + Size of the hashtable. More buckets use more memory but lower + the chance of hash hash collisions. ''; }; - tcpTimeout = mkOption { - type = types.int; - default = 120; + whitelistRatelimit = mkOption { + type = types.int; + default = 2000; description = '' - TCP timeout in seconds. + Max qps allowed from whitelisted sources. + 0 means unlimited. Set the rrl-whitelist option for specific + queries to apply this limit instead of the default to them. ''; }; - ipv4EDNSSize = mkOption { - type = types.int; - default = 4096; + }; + + + remoteControl = { + + enable = mkEnableOption "remote control via nsd-control"; + + controlCertFile = mkOption { + type = types.path; + default = "/etc/nsd/nsd_control.pem"; description = '' - Preferred EDNS buffer size for IPv4. + Path to the client certificate signed with the server certificate. + This file is used by nsd-control and generated by nsd-control-setup. ''; }; - ipv6EDNSSize = mkOption { - type = types.int; - default = 4096; + controlKeyFile = mkOption { + type = types.path; + default = "/etc/nsd/nsd_control.key"; description = '' - Preferred EDNS buffer size for IPv6. + Path to the client private key, which is used by nsd-control + but not by the server. This file is generated by nsd-control-setup. ''; }; - statistics = mkOption { - type = types.nullOr types.int; - default = null; + interfaces = mkOption { + type = types.listOf types.str; + default = [ "127.0.0.1" "::1" ]; description = '' - Statistics are produced every number of seconds. Prints to log. - If null no statistics are logged. + Which interfaces NSD should bind to for remote control. ''; }; - xfrdReloadTimeout = mkOption { - type = types.int; - default = 1; + port = mkOption { + type = types.int; + default = 8952; description = '' - Number of seconds between reloads triggered by xfrd. + Port number for remote control operations (uses TLS over TCP). ''; }; - zonefilesCheck = mkOption { - type = types.bool; - default = true; + serverCertFile = mkOption { + type = types.path; + default = "/etc/nsd/nsd_server.pem"; description = '' - Wheter to check mtime of all zone files on start and sighup. + Path to the server self signed certificate, which is used by the server + but and by nsd-control. This file is generated by nsd-control-setup. ''; }; - - extraConfig = mkOption { - type = types.str; - default = ""; + serverKeyFile = mkOption { + type = types.path; + default = "/etc/nsd/nsd_server.key"; description = '' - Extra nsd config. + Path to the server private key, which is used by the server + but not by nsd-control. This file is generated by nsd-control-setup. ''; }; + }; - ratelimit = { - enable = mkEnableOption "ratelimit capabilities"; - - size = mkOption { - type = types.int; - default = 1000000; - description = '' - Size of the hashtable. More buckets use more memory but lower - the chance of hash hash collisions. - ''; - }; - - ratelimit = mkOption { - type = types.int; - default = 200; - description = '' - Max qps allowed from any query source. - 0 means unlimited. With an verbosity of 2 blocked and - unblocked subnets will be logged. - ''; - }; - - whitelistRatelimit = mkOption { - type = types.int; - default = 2000; - description = '' - Max qps allowed from whitelisted sources. - 0 means unlimited. Set the rrl-whitelist option for specific - queries to apply this limit instead of the default to them. - ''; - }; - - slip = mkOption { - type = types.nullOr types.int; - default = null; - description = '' - Number of packets that get discarded before replying a SLIP response. - 0 disables SLIP responses. 1 will make every response a SLIP response. - ''; - }; - - ipv4PrefixLength = mkOption { - type = types.nullOr types.int; - default = null; - description = '' - IPv4 prefix length. Addresses are grouped by netblock. - ''; - }; - - ipv6PrefixLength = mkOption { - type = types.nullOr types.int; - default = null; - description = '' - IPv6 prefix length. Addresses are grouped by netblock. - ''; - }; - }; - - - remoteControl = { - enable = mkEnableOption "remote control via nsd-control"; - - interfaces = mkOption { - type = types.listOf types.str; - default = [ "127.0.0.1" "::1" ]; - description = '' - Which interfaces NSD should bind to for remote control. - ''; - }; - - port = mkOption { - type = types.int; - default = 8952; - description = '' - Port number for remote control operations (uses TLS over TCP). - ''; - }; - - serverKeyFile = mkOption { - type = types.path; - default = "/etc/nsd/nsd_server.key"; - description = '' - Path to the server private key, which is used by the server - but not by nsd-control. This file is generated by nsd-control-setup. - ''; - }; - - serverCertFile = mkOption { - type = types.path; - default = "/etc/nsd/nsd_server.pem"; - description = '' - Path to the server self signed certificate, which is used by the server - but and by nsd-control. This file is generated by nsd-control-setup. - ''; - }; - - controlKeyFile = mkOption { - type = types.path; - default = "/etc/nsd/nsd_control.key"; - description = '' - Path to the client private key, which is used by nsd-control - but not by the server. This file is generated by nsd-control-setup. - ''; - }; - - controlCertFile = mkOption { - type = types.path; - default = "/etc/nsd/nsd_control.pem"; - description = '' - Path to the client certificate signed with the server certificate. - This file is used by nsd-control and generated by nsd-control-setup. - ''; - }; - }; - - - keys = mkOption { - type = types.attrsOf (types.submodule { - options = { - algorithm = mkOption { - type = types.str; - default = "hmac-sha256"; - description = '' - Authentication algorithm for this key. - ''; - }; - - keyFile = mkOption { - type = types.path; - description = '' - Path to the file which contains the actual base64 encoded - key. The key will be copied into "${stateDir}/private" before - NSD starts. The copied file is only accessibly by the NSD - user. - ''; - }; - }; - }); - default = {}; - example = { - "tsig.example.org" = { - algorithm = "hmac-md5"; - secret = "aaaaaabbbbbbccccccdddddd"; - }; - }; - description = '' - Define your TSIG keys here. - ''; - }; - zones = mkOption { - type = types.attrsOf zoneOptions; - default = {}; - example = { - "serverGroup1" = { + zones = mkOption { + type = types.attrsOf zoneOptions; + default = {}; + example = literalExample '' + { "serverGroup1" = { provideXFR = [ "10.1.2.3 NOKEY" ]; children = { "example.com." = { - data = '' + data = ''' $ORIGIN example.com. $TTL 86400 @ IN SOA a.ns.example.com. admin.example.com. ( ... - ''; + '''; }; "example.org." = { - data = '' + data = ''' $ORIGIN example.org. $TTL 86400 @ IN SOA a.ns.example.com. admin.example.com. ( ... - ''; + '''; }; }; }; "example.net." = { provideXFR = [ "10.3.2.1 NOKEY" ]; - data = ''...''; + data = ''' + ... + '''; }; - }; - description = '' - Define your zones here. Zones can cascade other zones and therefore - inherit settings from parent zones. Look at the definition of - children to learn about inheritance and child zones. - The given example will define 3 zones (example.(com|org|net).). Both - example.com. and example.org. inherit their configuration from - serverGroup1. - ''; - }; - + } + ''; + description = '' + Define your zones here. Zones can cascade other zones and therefore + inherit settings from parent zones. Look at the definition of + children to learn about inheritance and child zones. + The given example will define 3 zones (example.(com|org|net).). Both + example.com. and example.org. inherit their configuration from + serverGroup1. + ''; }; + }; config = mkIf cfg.enable { users.extraGroups = singleton { name = username; - gid = config.ids.gids.nsd; + gid = config.ids.gids.nsd; }; users.extraUsers = singleton { - name = username; + name = username; description = "NSD service user"; - home = stateDir; + home = stateDir; createHome = true; - uid = config.ids.uids.nsd; - group = username; + uid = config.ids.uids.nsd; + group = username; }; systemd.services.nsd = { description = "NSD authoritative only domain name service"; - wantedBy = [ "multi-user.target" ]; - after = [ "network.target" ]; + + after = [ "keys.target" "network.target" ]; + wantedBy = [ "multi-user.target" ]; + wants = [ "keys.target" ]; serviceConfig = { - PIDFile = pidFile; - Restart = "always"; - ExecStart = "${nsdPkg}/sbin/nsd -d -c ${configFile}"; + ExecStart = "${nsdPkg}/sbin/nsd -d -c ${nsdEnv}/nsd.conf"; + PIDFile = pidFile; + Restart = "always"; + RestartSec = "4s"; + StartLimitBurst = 4; + StartLimitInterval = "5min"; }; preStart = '' - ${pkgs.coreutils}/bin/mkdir -m 0700 -p "${stateDir}/private" - ${pkgs.coreutils}/bin/mkdir -m 0700 -p "${stateDir}/tmp" - ${pkgs.coreutils}/bin/mkdir -m 0700 -p "${stateDir}/var" + rm -Rf "${stateDir}/private/" + rm -Rf "${stateDir}/tmp/" - ${pkgs.coreutils}/bin/touch "${stateDir}/don't touch anything in here" + mkdir -m 0700 -p "${stateDir}/private" + mkdir -m 0700 -p "${stateDir}/tmp" + mkdir -m 0700 -p "${stateDir}/var" - ${pkgs.coreutils}/bin/rm -f "${stateDir}/private/"* - ${pkgs.coreutils}/bin/rm -f "${stateDir}/tmp/"* + cat > "${stateDir}/don't touch anything in here" << EOF + Everything in this directory except NSD's state in var is + automatically generated and will be purged and redeployed + by the nsd.service pre-start script. + EOF - ${pkgs.coreutils}/bin/chown nsd:nsd -R "${stateDir}/private" - ${pkgs.coreutils}/bin/chown nsd:nsd -R "${stateDir}/tmp" - ${pkgs.coreutils}/bin/chown nsd:nsd -R "${stateDir}/var" + chown ${username}:${username} -R "${stateDir}/private" + chown ${username}:${username} -R "${stateDir}/tmp" + chown ${username}:${username} -R "${stateDir}/var" - ${pkgs.coreutils}/bin/rm -rf "${stateDir}/zones" - ${pkgs.coreutils}/bin/cp -r "${zoneFiles}" "${stateDir}/zones" + rm -rf "${stateDir}/zones" + cp -rL "${nsdEnv}/zones" "${stateDir}/zones" ${copyKeys} ''; diff --git a/nixos/modules/services/networking/pdnsd.nix b/nixos/modules/services/networking/pdnsd.nix new file mode 100644 index 000000000000..f4467b818958 --- /dev/null +++ b/nixos/modules/services/networking/pdnsd.nix @@ -0,0 +1,93 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let + cfg = config.services.pdnsd; + pdnsd = pkgs.pdnsd; + pdnsdUser = "pdnsd"; + pdnsdGroup = "pdnsd"; + pdnsdConf = pkgs.writeText "pdnsd.conf" + '' + global { + run_as=${pdnsdUser}; + cache_dir="${cfg.cacheDir}"; + ${cfg.globalConfig} + } + + server { + ${cfg.serverConfig} + } + ${cfg.extraConfig} + ''; +in + +{ options = + { services.pdnsd = + { enable = mkEnableOption "pdnsd"; + + cacheDir = mkOption { + type = types.str; + default = "/var/cache/pdnsd"; + description = "Directory holding the pdnsd cache"; + }; + + globalConfig = mkOption { + type = types.lines; + default = ""; + description = '' + Global configuration that should be added to the global directory + of <literal>pdnsd.conf</literal>. + ''; + }; + + serverConfig = mkOption { + type = types.lines; + default = ""; + description = '' + Server configuration that should be added to the server directory + of <literal>pdnsd.conf</literal>. + ''; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + description = '' + Extra configuration directives that should be added to + <literal>pdnsd.conf</literal>. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + users.extraUsers = singleton { + name = pdnsdUser; + uid = config.ids.uids.pdnsd; + group = pdnsdGroup; + description = "pdnsd user"; + }; + + users.extraGroups = singleton { + name = pdnsdGroup; + gid = config.ids.gids.pdnsd; + }; + + systemd.services.pdnsd = + { wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + preStart = + '' + mkdir -p "${cfg.cacheDir}" + touch "${cfg.cacheDir}/pdnsd.cache" + chown -R ${pdnsdUser}:${pdnsdGroup} "${cfg.cacheDir}" + ''; + description = "pdnsd"; + serviceConfig = + { + ExecStart = "${pdnsd}/bin/pdnsd -c ${pdnsdConf}"; + }; + }; + }; +} diff --git a/nixos/modules/services/networking/softether.nix b/nixos/modules/services/networking/softether.nix index a421b32f02c2..5e49efc3aa3a 100644 --- a/nixos/modules/services/networking/softether.nix +++ b/nixos/modules/services/networking/softether.nix @@ -61,11 +61,14 @@ in dataDir = cfg.dataDir; })) ]; - systemd.services.softether = { - description = "SoftEther VPN services initial job"; - after = [ "network-interfaces.target" ]; - wantedBy = [ "multi-user.target" ]; - preStart = '' + systemd.services."softether-init" = { + description = "SoftEther VPN services initial task"; + wantedBy = [ "network-interfaces.target" ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = false; + }; + script = '' for d in vpnserver vpnbridge vpnclient vpncmd; do if ! test -e ${cfg.dataDir}/$d; then ${pkgs.coreutils}/bin/mkdir -m0700 -p ${cfg.dataDir}/$d @@ -81,12 +84,12 @@ in (mkIf (cfg.vpnserver.enable) { systemd.services.vpnserver = { description = "SoftEther VPN Server"; - after = [ "network-interfaces.target" ]; - wantedBy = [ "multi-user.target" ]; + after = [ "softether-init.service" ]; + wantedBy = [ "network-interfaces.target" ]; serviceConfig = { + Type = "forking"; ExecStart = "${pkg}/bin/vpnserver start"; ExecStop = "${pkg}/bin/vpnserver stop"; - Type = "forking"; }; preStart = '' rm -rf ${cfg.dataDir}/vpnserver/vpnserver @@ -101,12 +104,12 @@ in (mkIf (cfg.vpnbridge.enable) { systemd.services.vpnbridge = { description = "SoftEther VPN Bridge"; - after = [ "network-interfaces.target" ]; - wantedBy = [ "multi-user.target" ]; + after = [ "softether-init.service" ]; + wantedBy = [ "network-interfaces.target" ]; serviceConfig = { + Type = "forking"; ExecStart = "${pkg}/bin/vpnbridge start"; ExecStop = "${pkg}/bin/vpnbridge stop"; - Type = "forking"; }; preStart = '' rm -rf ${cfg.dataDir}/vpnbridge/vpnbridge @@ -121,12 +124,12 @@ in (mkIf (cfg.vpnclient.enable) { systemd.services.vpnclient = { description = "SoftEther VPN Client"; - after = [ "network-interfaces.target" ]; - wantedBy = [ "multi-user.target" ]; + after = [ "softether-init.service" ]; + wantedBy = [ "network-interfaces.target" ]; serviceConfig = { + Type = "forking"; ExecStart = "${pkg}/bin/vpnclient start"; ExecStop = "${pkg}/bin/vpnclient stop"; - Type = "forking"; }; preStart = '' rm -rf ${cfg.dataDir}/vpnclient/vpnclient diff --git a/nixos/modules/services/networking/ssh/sshd.nix b/nixos/modules/services/networking/ssh/sshd.nix index 5baea4bc6aea..5971a5a250d3 100644 --- a/nixos/modules/services/networking/ssh/sshd.nix +++ b/nixos/modules/services/networking/ssh/sshd.nix @@ -52,6 +52,8 @@ let )); in listToAttrs (map mkAuthKeyFile usersWithKeys); + supportOldHostKeys = !versionAtLeast config.system.stateVersion "15.07"; + in { @@ -177,7 +179,7 @@ in default = [ { type = "rsa"; bits = 4096; path = "/etc/ssh/ssh_host_rsa_key"; } { type = "ed25519"; path = "/etc/ssh/ssh_host_ed25519_key"; } - ] ++ optionals (!versionAtLeast config.system.stateVersion "15.07") + ] ++ optionals supportOldHostKeys [ { type = "dsa"; path = "/etc/ssh/ssh_host_dsa_key"; } { type = "ecdsa"; bits = 521; path = "/etc/ssh/ssh_host_ecdsa_key"; } ]; @@ -261,7 +263,7 @@ in serviceConfig = { ExecStart = - "${cfgc.package}/sbin/sshd " + (optionalString cfg.startWhenNeeded "-i ") + + "${cfgc.package}/bin/sshd " + (optionalString cfg.startWhenNeeded "-i ") + "-f ${pkgs.writeText "sshd_config" cfg.extraConfig}"; KillMode = "process"; } // (if cfg.startWhenNeeded then { @@ -302,7 +304,7 @@ in services.openssh.authorizedKeysFiles = [ ".ssh/authorized_keys" ".ssh/authorized_keys2" "/etc/ssh/authorized_keys.d/%u" ]; - services.openssh.extraConfig = + services.openssh.extraConfig = mkOrder 0 '' PidFile /run/sshd.pid @@ -347,6 +349,15 @@ in ${flip concatMapStrings cfg.hostKeys (k: '' HostKey ${k.path} '')} + + # Allow DSA client keys for now. (These were deprecated + # in OpenSSH 7.0.) + PubkeyAcceptedKeyTypes +ssh-dss + + # Re-enable DSA host keys for now. + ${optionalString supportOldHostKeys '' + HostKeyAlgorithms +ssh-dss + ''} ''; assertions = [{ assertion = if cfg.forwardX11 then cfgc.setXAuthLocation else true; diff --git a/nixos/modules/services/networking/syncthing.nix b/nixos/modules/services/networking/syncthing.nix index f5d5e1d25561..67b90516b996 100644 --- a/nixos/modules/services/networking/syncthing.nix +++ b/nixos/modules/services/networking/syncthing.nix @@ -33,6 +33,17 @@ in ''; }; + all_proxy = mkOption { + type = types.string; + default = ""; + example = "socks5://address.com:1234"; + description = '' + Overwrites all_proxy environment variable for the syncthing process to + the given value. This is normaly used to let relay client connect + through SOCKS5 proxy server. + ''; + }; + dataDir = mkOption { default = "/var/lib/syncthing"; description = '' @@ -51,7 +62,6 @@ in }; - }; }; @@ -66,8 +76,13 @@ in description = "Syncthing service"; after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; - environment.STNORESTART = "yes"; # do not self-restart - environment.STNOUPGRADE = "yes"; + environment = { + STNORESTART = "yes"; # do not self-restart + STNOUPGRADE = "yes"; + } // + (config.networking.proxy.envVars) // + (if cfg.all_proxy != "" then { all_proxy = cfg.all_proxy; } else {}); + serviceConfig = { User = "${cfg.user}"; PermissionsStartOnly = true; diff --git a/nixos/modules/services/networking/tinc.nix b/nixos/modules/services/networking/tinc.nix index 34f4f6b37b60..9330e6c92ba8 100644 --- a/nixos/modules/services/networking/tinc.nix +++ b/nixos/modules/services/networking/tinc.nix @@ -95,6 +95,16 @@ in ''; }; + chroot = mkOption { + default = true; + type = types.bool; + description = '' + Change process root directory to the directory where the config file is located (/etc/tinc/netname/), for added security. + The chroot is performed after all the initialization is done, after writing pid files and opening network sockets. + + Note that tinc can't run scripts anymore (such as tinc-down or host-up), unless it is setup to be runnable inside chroot environment. + ''; + }; }; }; }; @@ -166,7 +176,7 @@ in fi ''; script = '' - tincd -D -U tinc.${network} -n ${network} --pidfile /run/tinc.${network}.pid -d ${toString data.debugLevel} + tincd -D -U tinc.${network} -n ${network} ${optionalString (data.chroot) "-R"} --pidfile /run/tinc.${network}.pid -d ${toString data.debugLevel} ''; }) ); diff --git a/nixos/modules/services/networking/tlsdated.nix b/nixos/modules/services/networking/tlsdated.nix index ff7d0178a81a..757cce287607 100644 --- a/nixos/modules/services/networking/tlsdated.nix +++ b/nixos/modules/services/networking/tlsdated.nix @@ -26,6 +26,7 @@ in extraOptions = mkOption { type = types.string; + default = ""; description = '' Additional command line arguments to pass to tlsdated. ''; diff --git a/nixos/modules/services/networking/unbound.nix b/nixos/modules/services/networking/unbound.nix index 73b10c1d5611..89762fe52488 100644 --- a/nixos/modules/services/networking/unbound.nix +++ b/nixos/modules/services/networking/unbound.nix @@ -16,6 +16,11 @@ let "forward-zone:\n name: .\n" + concatMapStrings (x: " forward-addr: ${x}\n") cfg.forwardAddresses; + rootTrustAnchorFile = "${stateDir}/root.key"; + + trustAnchor = optionalString cfg.enableRootTrustAnchor + "auto-trust-anchor-file: ${rootTrustAnchorFile}"; + confFile = pkgs.writeText "unbound.conf" '' server: directory: "${stateDir}" @@ -24,6 +29,7 @@ let pidfile: "" ${interfaces} ${access} + ${trustAnchor} ${cfg.extraConfig} ${forward} ''; @@ -38,28 +44,39 @@ in services.unbound = { enable = mkOption { - default = false; - description = "Whether to enable the Unbound domain name server."; + default = false; + type = types.bool; + description = "Whether to enable the Unbound domain name server."; }; allowedAccess = mkOption { - default = ["127.0.0.0/24"]; - description = "What networks are allowed to use unbound as a resolver."; + default = ["127.0.0.0/24"]; + type = types.listOf types.str; + description = "What networks are allowed to use unbound as a resolver."; }; interfaces = mkOption { - default = [ "127.0.0.1" "::1" ]; - description = "What addresses the server should listen on."; + default = [ "127.0.0.1" "::1" ]; + type = types.listOf types.str; + description = "What addresses the server should listen on."; }; forwardAddresses = mkOption { - default = [ ]; - description = "What servers to forward queries to."; + default = [ ]; + type = types.listOf types.str; + description = "What servers to forward queries to."; + }; + + enableRootTrustAnchor = mkOption { + default = true; + type = types.bool; + description = "Use and update root trust anchor for DNSSEC validation."; }; extraConfig = mkOption { - default = ""; - description = "Extra lines of unbound config."; + default = ""; + type = types.str; + description = "Extra lines of unbound config."; }; }; @@ -88,14 +105,15 @@ in preStart = '' mkdir -m 0755 -p ${stateDir}/dev/ - cp ${confFile} ${stateDir}/unbound.conf - chown unbound ${stateDir} - touch ${stateDir}/dev/random + cp ${confFile} ${stateDir}/unbound.conf + ${pkgs.unbound}/bin/unbound-anchor -a ${rootTrustAnchorFile} + chown unbound ${stateDir} ${rootTrustAnchorFile} + touch ${stateDir}/dev/random ${pkgs.utillinux}/bin/mount --bind -n /dev/random ${stateDir}/dev/random ''; serviceConfig = { - ExecStart = "${pkgs.unbound}/sbin/unbound -d -c ${stateDir}/unbound.conf"; + ExecStart = "${pkgs.unbound}/bin/unbound -d -c ${stateDir}/unbound.conf"; ExecStopPost="${pkgs.utillinux}/bin/umount ${stateDir}/dev/random"; }; }; diff --git a/nixos/modules/services/networking/unifi.nix b/nixos/modules/services/networking/unifi.nix index be8f12ecd32d..4dc0cd96904c 100644 --- a/nixos/modules/services/networking/unifi.nix +++ b/nixos/modules/services/networking/unifi.nix @@ -61,6 +61,8 @@ in partOf = systemdMountPoints; bindsTo = systemdMountPoints; unitConfig.RequiresMountsFor = stateDir; + # This a HACK to fix missing dependencies of dynamic libs extracted from jars + environment.LD_LIBRARY_PATH = with pkgs.stdenv; "${cc.cc}/lib"; preStart = '' # Ensure privacy of state diff --git a/nixos/modules/services/networking/wpa_supplicant.nix b/nixos/modules/services/networking/wpa_supplicant.nix index 1b655af6c82d..a8f445a2c73c 100644 --- a/nixos/modules/services/networking/wpa_supplicant.nix +++ b/nixos/modules/services/networking/wpa_supplicant.nix @@ -8,11 +8,15 @@ let ${optionalString cfg.userControlled.enable '' ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=${cfg.userControlled.group} update_config=1''} - ${concatStringsSep "\n" (mapAttrsToList (ssid: networkConfig: '' + ${concatStringsSep "\n" (mapAttrsToList (ssid: networkConfig: let + psk = if networkConfig.psk != null + then ''"${networkConfig.psk}"'' + else networkConfig.pskRaw; + in '' network={ ssid="${ssid}" - ${optionalString (networkConfig.psk != null) ''psk="${networkConfig.psk}"''} - ${optionalString (networkConfig.psk == null) ''key_mgmt=NONE''} + ${optionalString (psk != null) ''psk=${psk}''} + ${optionalString (psk == null) ''key_mgmt=NONE''} } '') cfg.networks)} '' else "/etc/wpa_supplicant.conf"; @@ -49,6 +53,19 @@ in { Be aware that these will be written to the nix store in plaintext! + + Mutually exclusive with <varname>pskRaw</varname>. + ''; + }; + + pskRaw = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + The network's pre-shared key in hex defaulting + to being a network without any authentication. + + Mutually exclusive with <varname>psk</varname>. ''; }; }; @@ -61,10 +78,11 @@ in { ''; default = {}; example = literalExample '' - echelon = { - psk = "abcdefgh"; - }; - "free.wifi" = {}; + { echelon = { + psk = "abcdefgh"; + }; + "free.wifi" = {}; + } ''; }; @@ -95,6 +113,11 @@ in { config = mkMerge [ (mkIf cfg.enable { + assertions = flip mapAttrsToList cfg.networks (name: cfg: { + assertion = cfg.psk == null || cfg.pskRaw == null; + message = ''networking.wireless."${name}".psk and networking.wireless."${name}".pskRaw are mutually exclusive''; + }); + environment.systemPackages = [ pkgs.wpa_supplicant ]; services.dbus.packages = [ pkgs.wpa_supplicant ]; @@ -105,6 +128,7 @@ in { in { description = "WPA Supplicant"; + after = [ "network-interfaces.target" ]; wantedBy = [ "network.target" ]; path = [ pkgs.wpa_supplicant ]; diff --git a/nixos/modules/services/networking/zerotierone.nix b/nixos/modules/services/networking/zerotierone.nix index 886ea18d9809..6237f95b127b 100644 --- a/nixos/modules/services/networking/zerotierone.nix +++ b/nixos/modules/services/networking/zerotierone.nix @@ -21,10 +21,9 @@ in chown -R root:root /var/lib/zerotier-one ''; serviceConfig = { - Type = "forking"; - User = "root"; - PIDFile = "/var/lib/zerotier-one/zerotier-one.pid"; - ExecStart = "${pkgs.zerotierone}/bin/zerotier-one -d"; + ExecStart = "${pkgs.zerotierone}/bin/zerotier-one"; + Restart = "always"; + KillMode = "process"; }; }; environment.systemPackages = [ pkgs.zerotierone ]; diff --git a/nixos/modules/services/printing/cupsd.nix b/nixos/modules/services/printing/cupsd.nix index 0fe25b66da08..59416560655d 100644 --- a/nixos/modules/services/printing/cupsd.nix +++ b/nixos/modules/services/printing/cupsd.nix @@ -4,38 +4,104 @@ with lib; let - inherit (pkgs) cups cups_filters; + inherit (pkgs) cups cups-pk-helper cups_filters gutenprint; cfg = config.services.printing; + avahiEnabled = config.services.avahi.enable; + polkitEnabled = config.security.polkit.enable; + additionalBackends = pkgs.runCommand "additional-cups-backends" { } '' mkdir -p $out - if [ ! -e ${cups}/lib/cups/backend/smb ]; then + if [ ! -e ${cups.out}/lib/cups/backend/smb ]; then mkdir -p $out/lib/cups/backend ln -sv ${pkgs.samba}/bin/smbspool $out/lib/cups/backend/smb fi # Provide support for printing via HTTPS. - if [ ! -e ${cups}/lib/cups/backend/https ]; then + if [ ! -e ${cups.out}/lib/cups/backend/https ]; then mkdir -p $out/lib/cups/backend - ln -sv ${cups}/lib/cups/backend/ipp $out/lib/cups/backend/https + ln -sv ${cups.out}/lib/cups/backend/ipp $out/lib/cups/backend/https fi ''; # Here we can enable additional backends, filters, etc. that are not # part of CUPS itself, e.g. the SMB backend is part of Samba. Since - # we can't update ${cups}/lib/cups itself, we create a symlink tree + # we can't update ${cups.out}/lib/cups itself, we create a symlink tree # here and add the additional programs. The ServerBin directive in # cupsd.conf tells cupsd to use this tree. bindir = pkgs.buildEnv { name = "cups-progs"; - paths = cfg.drivers; - pathsToLink = [ "/lib/cups" "/share/cups" "/bin" "/etc/cups" ]; + paths = + [ cups additionalBackends cups_filters pkgs.ghostscript ] + ++ optional cfg.gutenprint gutenprint + ++ cfg.drivers; + pathsToLink = [ "/lib/cups" "/share/cups" "/bin" ]; postBuild = cfg.bindirCmds; ignoreCollisions = true; }; + writeConf = name: text: pkgs.writeTextFile { + inherit name text; + destination = "/etc/cups/${name}"; + }; + + cupsFilesFile = writeConf "cups-files.conf" '' + SystemGroup root wheel + + ServerBin ${bindir}/lib/cups + DataDir ${bindir}/share/cups + + AccessLog syslog + ErrorLog syslog + PageLog syslog + + TempDir ${cfg.tempDir} + + # User and group used to run external programs, including + # those that actually send the job to the printer. Note that + # Udev sets the group of printer devices to `lp', so we want + # these programs to run as `lp' as well. + User cups + Group lp + + ${cfg.extraFilesConf} + ''; + + cupsdFile = writeConf "cupsd.conf" '' + ${concatMapStrings (addr: '' + Listen ${addr} + '') cfg.listenAddresses} + Listen /var/run/cups/cups.sock + + SetEnv PATH ${bindir}/lib/cups/filter:${bindir}/bin + + DefaultShared ${if cfg.defaultShared then "Yes" else "No"} + + Browsing ${if cfg.browsing then "Yes" else "No"} + + WebInterface ${if cfg.webInterface then "Yes" else "No"} + + ${cfg.extraConf} + ''; + + browsedFile = writeConf "cups-browsed.conf" cfg.browsedConf; + + rootdir = pkgs.buildEnv { + name = "cups-progs"; + paths = [ + cupsFilesFile + cupsdFile + (writeConf "client.conf" cfg.clientConf) + (writeConf "snmp.conf" cfg.snmpConf) + ] ++ optional avahiEnabled browsedFile + ++ optional cfg.gutenprint gutenprint + ++ cfg.drivers; + pathsToLink = [ "/etc/cups" ]; + ignoreCollisions = true; + }; + in { @@ -96,25 +162,11 @@ in ''; }; - cupsdConf = mkOption { - type = types.lines; - default = ""; - example = - '' - BrowsePoll cups.example.com - LogLevel debug - ''; - description = '' - The contents of the configuration file of the CUPS daemon - (<filename>cupsd.conf</filename>). - ''; - }; - - cupsFilesConf = mkOption { + extraFilesConf = mkOption { type = types.lines; default = ""; description = '' - The contents of the configuration file of the CUPS daemon + Extra contents of the configuration file of the CUPS daemon (<filename>cups-files.conf</filename>). ''; }; @@ -171,8 +223,18 @@ in ''; }; + gutenprint = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable Gutenprint drivers for CUPS. This includes auto-updating + Gutenprint PPD files. + ''; + }; + drivers = mkOption { type = types.listOf types.path; + default = []; example = literalExample "[ pkgs.splix ]"; description = '' CUPS drivers to use. Drivers provided by CUPS, cups-filters, Ghostscript @@ -204,15 +266,10 @@ in description = "CUPS printing services"; }; - environment.systemPackages = [ cups ]; + environment.systemPackages = [ cups ] ++ optional polkitEnabled cups-pk-helper; + environment.etc."cups".source = "/var/lib/cups"; - environment.etc."cups/client.conf".text = cfg.clientConf; - environment.etc."cups/cups-files.conf".text = cfg.cupsFilesConf; - environment.etc."cups/cupsd.conf".text = cfg.cupsdConf; - environment.etc."cups/cups-browsed.conf".text = cfg.browsedConf; - environment.etc."cups/snmp.conf".text = cfg.snmpConf; - - services.dbus.packages = [ cups ]; + services.dbus.packages = [ cups ] ++ optional polkitEnabled cups-pk-helper; # Cups uses libusb to talk to printers, and does not use the # linux kernel driver. If the driver is not in a black list, it @@ -230,19 +287,35 @@ in preStart = '' - mkdir -m 0755 -p /etc/cups mkdir -m 0700 -p /var/cache/cups mkdir -m 0700 -p /var/spool/cups mkdir -m 0755 -p ${cfg.tempDir} - ''; - restartTriggers = - [ config.environment.etc."cups/cups-files.conf".source - config.environment.etc."cups/cupsd.conf".source - ]; + mkdir -m 0755 -p /var/lib/cups + # Backwards compatibility + if [ ! -L /etc/cups ]; then + mv /etc/cups/* /var/lib/cups + rmdir /etc/cups + ln -s /var/lib/cups /etc/cups + fi + # First, clean existing symlinks + if [ -n "$(ls /var/lib/cups)" ]; then + for i in /var/lib/cups/*; do + [ -L "$i" ] && rm "$i" + done + fi + # Then, populate it with static files + cd ${rootdir}/etc/cups + for i in *; do + [ ! -e "/var/lib/cups/$i" ] && ln -s "${rootdir}/etc/cups/$i" "/var/lib/cups/$i" + done + ${optionalString cfg.gutenprint '' + ${gutenprint}/bin/cups-genppdupdate -p /etc/cups/ppd + ''} + ''; }; - systemd.services.cups-browsed = mkIf config.services.avahi.enable + systemd.services.cups-browsed = mkIf avahiEnabled { description = "CUPS Remote Printer Discovery"; wantedBy = [ "multi-user.target" ]; @@ -255,54 +328,13 @@ in serviceConfig.ExecStart = "${cups_filters}/bin/cups-browsed"; - restartTriggers = - [ config.environment.etc."cups/cups-browsed.conf".source - ]; + restartTriggers = [ browsedFile ]; }; - services.printing.drivers = - [ cups pkgs.ghostscript pkgs.cups_filters additionalBackends - pkgs.perl pkgs.coreutils pkgs.gnused pkgs.bc pkgs.gawk pkgs.gnugrep - ]; - - services.printing.cupsFilesConf = - '' - SystemGroup root wheel - - ServerBin ${bindir}/lib/cups - DataDir ${bindir}/share/cups - - AccessLog syslog - ErrorLog syslog - PageLog syslog - - TempDir ${cfg.tempDir} - - # User and group used to run external programs, including - # those that actually send the job to the printer. Note that - # Udev sets the group of printer devices to `lp', so we want - # these programs to run as `lp' as well. - User cups - Group lp - ''; - - services.printing.cupsdConf = + services.printing.extraConf = '' LogLevel info - ${concatMapStrings (addr: '' - Listen ${addr} - '') cfg.listenAddresses} - Listen /var/run/cups/cups.sock - - SetEnv PATH ${bindir}/lib/cups/filter:${bindir}/bin:${bindir}/sbin - - DefaultShared ${if cfg.defaultShared then "Yes" else "No"} - - Browsing ${if cfg.browsing then "Yes" else "No"} - - WebInterface ${if cfg.webInterface then "Yes" else "No"} - DefaultAuthType Basic <Location /> @@ -343,8 +375,6 @@ in Order deny,allow </Limit> </Policy> - - ${cfg.extraConf} ''; security.pam.services.cups = {}; diff --git a/nixos/modules/services/search/elasticsearch.nix b/nixos/modules/services/search/elasticsearch.nix index e586600cdffd..c51a42b8e9c1 100644 --- a/nixos/modules/services/search/elasticsearch.nix +++ b/nixos/modules/services/search/elasticsearch.nix @@ -9,6 +9,8 @@ let network.host: ${cfg.listenAddress} network.port: ${toString cfg.port} network.tcp.port: ${toString cfg.tcp_port} + # TODO: find a way to enable security manager + security.manager.enabled: false cluster.name: ${cfg.cluster_name} ${cfg.extraConf} ''; @@ -39,8 +41,8 @@ in { package = mkOption { description = "Elasticsearch package to use."; - default = pkgs.elasticsearch; - defaultText = "pkgs.elasticsearch"; + default = pkgs.elasticsearch2; + defaultText = "pkgs.elasticsearch2"; type = types.package; }; @@ -128,7 +130,10 @@ in { description = "Elasticsearch Daemon"; wantedBy = [ "multi-user.target" ]; after = [ "network-interfaces.target" ]; - environment = { ES_HOME = cfg.dataDir; }; + path = [ pkgs.inetutils ]; + environment = { + ES_HOME = cfg.dataDir; + }; serviceConfig = { ExecStart = "${cfg.package}/bin/elasticsearch -Des.path.conf=${configDir} ${toString cfg.extraCmdLineOptions}"; User = "elasticsearch"; @@ -136,11 +141,11 @@ in { }; preStart = '' mkdir -m 0700 -p ${cfg.dataDir} - if [ "$(id -u)" = 0 ]; then chown -R elasticsearch ${cfg.dataDir}; fi # Install plugins - rm ${cfg.dataDir}/plugins || true - ln -s ${esPlugins}/plugins ${cfg.dataDir}/plugins + ln -sfT ${esPlugins}/plugins ${cfg.dataDir}/plugins + ln -sfT ${cfg.package}/lib ${cfg.dataDir}/lib + if [ "$(id -u)" = 0 ]; then chown -R elasticsearch ${cfg.dataDir}; fi ''; postStart = mkBefore '' until ${pkgs.curl.bin}/bin/curl -s -o /dev/null ${cfg.listenAddress}:${toString cfg.port}; do diff --git a/nixos/modules/services/search/kibana.nix b/nixos/modules/services/search/kibana.nix index 4263ed22a8db..033b8139d341 100644 --- a/nixos/modules/services/search/kibana.nix +++ b/nixos/modules/services/search/kibana.nix @@ -7,37 +7,33 @@ let cfgFile = pkgs.writeText "kibana.json" (builtins.toJSON ( (filterAttrsRecursive (n: v: v != null) ({ - server = { - host = cfg.listenAddress; - port = cfg.port; - ssl = { - cert = cfg.cert; - key = cfg.key; - }; - }; - - kibana = { - index = cfg.index; - defaultAppId = cfg.defaultAppId; - }; - - elasticsearch = { - url = cfg.elasticsearch.url; - username = cfg.elasticsearch.username; - password = cfg.elasticsearch.password; - ssl = { - cert = cfg.elasticsearch.cert; - key = cfg.elasticsearch.key; - ca = cfg.elasticsearch.ca; - }; - }; - - logging = { - verbose = cfg.logLevel == "verbose"; - quiet = cfg.logLevel == "quiet"; - silent = cfg.logLevel == "silent"; - dest = "stdout"; - }; + host = cfg.listenAddress; + port = cfg.port; + ssl_cert_file = cfg.cert; + ssl_key_file = cfg.key; + + kibana_index = cfg.index; + default_app_id = cfg.defaultAppId; + + elasticsearch_url = cfg.elasticsearch.url; + kibana_elasticsearch_username = cfg.elasticsearch.username; + kibana_elasticsearch_password = cfg.elasticsearch.password; + kibana_elasticsearch_cert = cfg.elasticsearch.cert; + kibana_elasticsearch_key = cfg.elasticsearch.key; + ca = cfg.elasticsearch.ca; + + bundled_plugin_ids = [ + "plugins/dashboard/index" + "plugins/discover/index" + "plugins/doc/index" + "plugins/kibana/index" + "plugins/markdown_vis/index" + "plugins/metric_vis/index" + "plugins/settings/index" + "plugins/table_vis/index" + "plugins/vis_types/index" + "plugins/visualize/index" + ]; } // cfg.extraConf) ))); in { @@ -118,12 +114,6 @@ in { }; }; - logLevel = mkOption { - description = "Kibana log level"; - default = "normal"; - type = types.enum ["verbose" "normal" "silent" "quiet"]; - }; - package = mkOption { description = "Kibana package to use"; default = pkgs.kibana; @@ -149,6 +139,7 @@ in { description = "Kibana Service"; wantedBy = [ "multi-user.target" ]; after = [ "network-interfaces.target" "elasticsearch.service" ]; + environment = { BABEL_CACHE_PATH = "${cfg.dataDir}/.babelcache.json"; }; serviceConfig = { ExecStart = "${cfg.package}/bin/kibana --config ${cfgFile}"; User = "kibana"; diff --git a/nixos/modules/services/security/clamav.nix b/nixos/modules/services/security/clamav.nix index 548aee29b266..e4e5c1253b77 100644 --- a/nixos/modules/services/security/clamav.nix +++ b/nixos/modules/services/security/clamav.nix @@ -16,6 +16,7 @@ let ${cfg.daemon.extraConfig} ''; + pkg = pkgs.clamav.override { freshclamConf = cfg.updater.config; }; in { options = { @@ -54,7 +55,7 @@ in }; config = mkIf cfg.updater.enable or cfg.daemon.enable { - environment.systemPackages = [ pkgs.clamav ]; + environment.systemPackages = [ pkg ]; users.extraUsers = singleton { name = clamavUser; uid = config.ids.uids.clamav; @@ -76,7 +77,7 @@ in systemd.services.clamd = mkIf cfg.daemon.enable { description = "ClamAV daemon (clamd)"; - path = [ pkgs.clamav ]; + path = [ pkg ]; after = [ "network.target" "freshclam.service" ]; requires = [ "freshclam.service" ]; wantedBy = [ "multi-user.target" ]; @@ -87,7 +88,7 @@ in chown ${clamavUser}:${clamavGroup} ${runDir} ''; serviceConfig = { - ExecStart = "${pkgs.clamav}/bin/clamd --config-file=${clamdConfigFile}"; + ExecStart = "${pkg}/bin/clamd --config-file=${clamdConfigFile}"; Type = "forking"; ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; Restart = "on-failure"; @@ -100,13 +101,13 @@ in description = "ClamAV updater (freshclam)"; after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; - path = [ pkgs.clamav ]; + path = [ pkg ]; preStart = '' mkdir -m 0755 -p ${stateDir} chown ${clamavUser}:${clamavGroup} ${stateDir} ''; serviceConfig = { - ExecStart = "${pkgs.clamav}/bin/freshclam --daemon --config-file=${pkgs.writeText "freshclam.conf" cfg.updater.config}"; + ExecStart = "${pkg}/bin/freshclam --daemon --config-file=${pkgs.writeText "freshclam.conf" cfg.updater.config}"; ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; Restart = "on-failure"; RestartSec = "10s"; diff --git a/nixos/modules/services/security/fail2ban.nix b/nixos/modules/services/security/fail2ban.nix index 1d3e18dcab25..afbd81be91f2 100644 --- a/nixos/modules/services/security/fail2ban.nix +++ b/nixos/modules/services/security/fail2ban.nix @@ -50,20 +50,20 @@ in jails = mkOption { default = { }; - example = - { "apache-nohome-iptables" = - '' - # Block an IP address if it accesses a non-existent - # home directory more than 5 times in 10 minutes, - # since that indicates that it's scanning. - filter = apache-nohome - action = iptables-multiport[name=HTTP, port="http,https"] - logpath = /var/log/httpd/error_log* - findtime = 600 - bantime = 600 - maxretry = 5 - ''; - }; + example = literalExample '' + { apache-nohome-iptables = ''' + # Block an IP address if it accesses a non-existent + # home directory more than 5 times in 10 minutes, + # since that indicates that it's scanning. + filter = apache-nohome + action = iptables-multiport[name=HTTP, port="http,https"] + logpath = /var/log/httpd/error_log* + findtime = 600 + bantime = 600 + maxretry = 5 + '''; + } + ''; type = types.attrsOf types.lines; description = '' diff --git a/nixos/modules/services/security/haka.nix b/nixos/modules/services/security/haka.nix new file mode 100644 index 000000000000..f48a79b1f7f1 --- /dev/null +++ b/nixos/modules/services/security/haka.nix @@ -0,0 +1,157 @@ +# This module defines global configuration for Haka. + +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.haka; + + haka = cfg.package; + + hakaConf = pkgs.writeText "haka.conf" + '' + [general] + configuration = ${if lib.strings.hasPrefix "/" cfg.configFile + then "${cfg.configFile}" + else "${haka}/share/haka/sample/${cfg.configFile}"} + ${optionalString (builtins.lessThan 0 cfg.threads) "thread = ${cfg.threads}"} + + [packet] + ${optionalString cfg.pcap ''module = "packet/pcap"''} + ${optionalString cfg.nfqueue ''module = "packet/nqueue"''} + ${optionalString cfg.dump.enable ''dump = "yes"''} + ${optionalString cfg.dump.enable ''dump_input = "${cfg.dump.input}"''} + ${optionalString cfg.dump.enable ''dump_output = "${cfg.dump.output}"''} + + interfaces = "${lib.strings.concatStringsSep "," cfg.interfaces}" + + [log] + # Select the log module + module = "log/syslog" + + # Set the default logging level + #level = "info,packet=debug" + + [alert] + # Select the alert module + module = "alert/syslog" + + # Disable alert on standard output + #alert_on_stdout = no + + # alert/file module option + #file = "/dev/null" + ''; + +in + +{ + + ###### interface + + options = { + + services.haka = { + + enable = mkEnableOption "Haka"; + + package = mkOption { + default = pkgs.haka; + defaultText = "pkgs.haka"; + type = types.package; + description = " + Which Haka derivation to use. + "; + }; + + configFile = mkOption { + default = "empty.lua"; + example = "/srv/haka/myfilter.lua"; + type = types.string; + description = '' + Specify which configuration file Haka uses. + It can be absolute path or a path relative to the sample directory of + the haka git repo. + ''; + }; + + interfaces = mkOption { + default = [ "eth0" ]; + example = [ "any" ]; + type = with types; listOf string; + description = '' + Specify which interface(s) Haka listens to. + Use 'any' to listen to all interfaces. + ''; + }; + + threads = mkOption { + default = 0; + example = 4; + type = types.int; + description = '' + The number of threads that will be used. + All system threads are used by default. + ''; + }; + + pcap = mkOption { + default = true; + example = false; + type = types.bool; + description = "Whether to enable pcap"; + }; + + nfqueue = mkEnableOption "nfqueue"; + + dump.enable = mkEnableOption "dump"; + dump.input = mkOption { + default = "/tmp/input.pcap"; + example = "/path/to/file.pcap"; + type = types.path; + description = "Path to file where incoming packets are dumped"; + }; + + dump.output = mkOption { + default = "/tmp/output.pcap"; + example = "/path/to/file.pcap"; + type = types.path; + description = "Path to file where outgoing packets are dumped"; + }; + }; + }; + + + ###### implementation + + config = mkIf cfg.enable { + + assertions = [ + { assertion = cfg.pcap != cfg.nfqueue; + message = "either pcap or nfqueue can be enabled, not both."; + } + { assertion = cfg.nfqueue -> !dump.enable; + message = "dump can only be used with nfqueue."; + } + { assertion = cfg.interfaces != []; + message = "at least one interface must be specified."; + }]; + + + environment.systemPackages = [ haka ]; + + systemd.services.haka = { + description = "Haka"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + serviceConfig = { + ExecStart = "${haka}/bin/haka -c ${hakaConf}"; + ExecStop = "${haka}/bin/hakactl stop"; + User = "root"; + Type = "forking"; + }; + }; + }; +} diff --git a/nixos/modules/services/system/dbus.nix b/nixos/modules/services/system/dbus.nix index 77427ce9606e..2e29ef6a8f53 100644 --- a/nixos/modules/services/system/dbus.nix +++ b/nixos/modules/services/system/dbus.nix @@ -121,7 +121,7 @@ in security.setuidOwners = singleton { program = "dbus-daemon-launch-helper"; - source = "${pkgs.dbus_daemon}/libexec/dbus-daemon-launch-helper"; + source = "${pkgs.dbus_daemon.lib}/libexec/dbus-daemon-launch-helper"; owner = "root"; group = "messagebus"; setuid = true; diff --git a/nixos/modules/services/system/uptimed.nix b/nixos/modules/services/system/uptimed.nix index 5f8916bbf9a4..b20d60968032 100644 --- a/nixos/modules/services/system/uptimed.nix +++ b/nixos/modules/services/system/uptimed.nix @@ -1,66 +1,55 @@ -{pkgs, config, lib, ...}: +{ config, lib, pkgs, ... }: -let - - inherit (lib) mkOption mkIf singleton; - - inherit (pkgs) uptimed; +with lib; +let + cfg = config.services.uptimed; stateDir = "/var/spool/uptimed"; - - uptimedUser = "uptimed"; - in - { - - ###### interface - options = { - services.uptimed = { - enable = mkOption { default = false; description = '' - Uptimed allows you to track your highest uptimes. + Enable <literal>uptimed</literal>, allowing you to track + your highest uptimes. ''; }; - }; - }; - - ###### implementation - - config = mkIf config.services.uptimed.enable { - - environment.systemPackages = [ uptimed ]; - - users.extraUsers = singleton - { name = uptimedUser; - uid = config.ids.uids.uptimed; - description = "Uptimed daemon user"; - home = stateDir; - }; + config = mkIf cfg.enable { + users.extraUsers.uptimed = { + description = "Uptimed daemon user"; + home = stateDir; + createHome = true; + uid = config.ids.uids.uptimed; + }; systemd.services.uptimed = { - description = "Uptimed daemon"; - wantedBy = [ "multi-user.target" ]; + unitConfig.Documentation = "man:uptimed(8) man:uprecords(1)"; + description = "uptimed service"; + wantedBy = [ "multi-user.target" ]; + + serviceConfig = { + Restart = "on-failure"; + User = "uptimed"; + Nice = 19; + IOSchedulingClass = "idle"; + PrivateTmp = "yes"; + PrivateNetwork = "yes"; + NoNewPrivileges = "yes"; + ReadWriteDirectories = stateDir; + InaccessibleDirectories = "/home"; + ExecStart = "${pkgs.uptimed}/sbin/uptimed -f -p ${stateDir}/pid"; + }; preStart = '' - mkdir -m 0755 -p ${stateDir} - chown ${uptimedUser} ${stateDir} - if ! test -f ${stateDir}/bootid ; then - ${uptimed}/sbin/uptimed -b + ${pkgs.uptimed}/sbin/uptimed -b fi ''; - - script = "${uptimed}/sbin/uptimed"; }; - }; - } diff --git a/nixos/modules/services/torrent/transmission.nix b/nixos/modules/services/torrent/transmission.nix index c4dc6512a0dd..59ef915af6d4 100644 --- a/nixos/modules/services/torrent/transmission.nix +++ b/nixos/modules/services/torrent/transmission.nix @@ -21,7 +21,7 @@ let else toString ''"${x}"''; # for users in group "transmission" to have access to torrents - fullSettings = { download-dir = downloadDir; incomplete-dir = incompleteDir; } // cfg.settings // { umask = 2; }; + fullSettings = { umask = 2; download-dir = downloadDir; incomplete-dir = incompleteDir; } // cfg.settings; in { options = { diff --git a/nixos/modules/services/web-servers/apache-httpd/default.nix b/nixos/modules/services/web-servers/apache-httpd/default.nix index 880cd9f39c44..760d7e147d7c 100644 --- a/nixos/modules/services/web-servers/apache-httpd/default.nix +++ b/nixos/modules/services/web-servers/apache-httpd/default.nix @@ -12,7 +12,7 @@ let httpdConf = mainCfg.configFile; - php = pkgs.php.override { apacheHttpd = httpd; }; + php = pkgs.php.override { apacheHttpd = httpd.dev; /* otherwise it only gets .out */ }; getPort = cfg: if cfg.port != 0 then cfg.port else if cfg.enableSSL then 443 else 80; @@ -173,7 +173,8 @@ let SSLRandomSeed connect builtin SSLProtocol All -SSLv2 -SSLv3 - SSLCipherSuite HIGH:MEDIUM:!aNULL:!MD5:!EXP + SSLCipherSuite HIGH:!aNULL:!MD5:!EXP + SSLHonorCipherOrder on ''; diff --git a/nixos/modules/services/web-servers/apache-httpd/mediawiki.nix b/nixos/modules/services/web-servers/apache-httpd/mediawiki.nix index 52d8c89baff2..0fe8d1a89cf3 100644 --- a/nixos/modules/services/web-servers/apache-httpd/mediawiki.nix +++ b/nixos/modules/services/web-servers/apache-httpd/mediawiki.nix @@ -83,11 +83,11 @@ let # Unpack Mediawiki and put the config file in its root directory. mediawikiRoot = pkgs.stdenv.mkDerivation rec { - name= "mediawiki-1.23.9"; + name= "mediawiki-1.23.13"; src = pkgs.fetchurl { url = "http://download.wikimedia.org/mediawiki/1.23/${name}.tar.gz"; - sha256 = "1l7k4g0pgz92yvrfr52w26x740s4362v0gc95pk0i30vn2sp5bql"; + sha256 = "168wpf53n4ksj2g5q5r0hxapx6238dvsfng5ff9ixk6axsn0j5d0"; }; skins = config.skins; diff --git a/nixos/modules/services/web-servers/apache-httpd/moodle.nix b/nixos/modules/services/web-servers/apache-httpd/moodle.nix index 84c8281ecd8d..87b1fba5aa10 100644 --- a/nixos/modules/services/web-servers/apache-httpd/moodle.nix +++ b/nixos/modules/services/web-servers/apache-httpd/moodle.nix @@ -46,16 +46,16 @@ let ''; # Unpack Moodle and put the config file in its root directory. moodleRoot = pkgs.stdenv.mkDerivation rec { - name= "moodle-2.8.5"; + name= "moodle-2.8.10"; src = pkgs.fetchurl { url = "https://download.moodle.org/stable28/${name}.tgz"; - sha256 = "1a159a193010cddedce10ee009184502e6f732e4d7c85167d8597fe5dff9e190"; + sha256 = "0c3r5081ipcwc9s6shakllnrkd589y2ln5z5m1q09l4h6a7cy4z2"; }; buildPhase = '' - ''; + ''; installPhase = '' @@ -132,7 +132,7 @@ in cleartext in the Nix store! ''; }; - + dbPrefix = mkOption { default = "mdl_"; example = "my_other_mdl_"; @@ -158,7 +158,7 @@ in type = types.path; }; - + extraConfig = mkOption { default = ""; example = diff --git a/nixos/modules/services/web-servers/apache-httpd/owncloud.nix b/nixos/modules/services/web-servers/apache-httpd/owncloud.nix index 9994de0f9b40..94e85f1f4289 100644 --- a/nixos/modules/services/web-servers/apache-httpd/owncloud.nix +++ b/nixos/modules/services/web-servers/apache-httpd/owncloud.nix @@ -333,7 +333,7 @@ let 'version' => '${config.package.version}', - 'openssl' => '${pkgs.openssl}/bin/openssl' + 'openssl' => '${pkgs.openssl.bin}/bin/openssl' ); @@ -370,6 +370,7 @@ rec { package = mkOption { type = types.package; default = pkgs.owncloud70; + defaultText = "pkgs.owncloud70"; example = literalExample "pkgs.owncloud70"; description = '' PostgreSQL package to use. diff --git a/nixos/modules/services/web-servers/phpfpm.nix b/nixos/modules/services/web-servers/phpfpm.nix index bdd41ed702b5..6a60000ce19e 100644 --- a/nixos/modules/services/web-servers/phpfpm.nix +++ b/nixos/modules/services/web-servers/phpfpm.nix @@ -46,24 +46,26 @@ in { phpIni = mkOption { type = types.path; - description = "PHP configuration file to use."; + default = "${cfg.phpPackage}/etc/php-recommended.ini"; + description = "php.ini file to use."; }; poolConfigs = mkOption { type = types.attrsOf types.lines; default = {}; - example = { - mypool = '' - listen = /run/phpfpm/mypool - user = nobody - pm = dynamic - pm.max_children = 75 - pm.start_servers = 10 - pm.min_spare_servers = 5 - pm.max_spare_servers = 20 - pm.max_requests = 500 - ''; - }; + example = literalExample '' + { mypool = ''' + listen = /run/phpfpm/mypool + user = nobody + pm = dynamic + pm.max_children = 75 + pm.start_servers = 10 + pm.min_spare_servers = 5 + pm.max_spare_servers = 20 + pm.max_requests = 500 + '''; + } + ''; description = '' A mapping between PHP FPM pool names and their configurations. See the documentation on <literal>php-fpm.conf</literal> for @@ -87,7 +89,5 @@ in { }; }; - services.phpfpm.phpIni = mkDefault "${cfg.phpPackage}/etc/php-recommended.ini"; - }; } diff --git a/nixos/modules/services/web-servers/uwsgi.nix b/nixos/modules/services/web-servers/uwsgi.nix index 3e18a6f0e986..e6c25e6215c1 100644 --- a/nixos/modules/services/web-servers/uwsgi.nix +++ b/nixos/modules/services/web-servers/uwsgi.nix @@ -5,43 +5,85 @@ with lib; let cfg = config.services.uwsgi; - python2Pkgs = pkgs.python2Packages.override { - python = pkgs.uwsgi.python2; - self = python2Pkgs; + uwsgi = pkgs.uwsgi.override { + plugins = cfg.plugins; }; - python3Pkgs = pkgs.python3Packages.override { - python = pkgs.uwsgi.python3; - self = python3Pkgs; - }; + buildCfg = name: c: + let + plugins = + if any (n: !any (m: m == n) cfg.plugins) (c.plugins or []) + then throw "`plugins` attribute in UWSGI configuration contains plugins not in config.services.uwsgi.plugins" + else c.plugins or cfg.plugins; + + hasPython = v: filter (n: n == "python${v}") plugins != []; + hasPython2 = hasPython "2"; + hasPython3 = hasPython "3"; + + python = + if hasPython2 && hasPython3 then + throw "`plugins` attribute in UWSGI configuration shouldn't contain both python2 and python3" + else if hasPython2 then uwsgi.python2 + else if hasPython3 then uwsgi.python3 + else null; + + pythonPackages = pkgs.pythonPackages.override { + inherit python; + self = pythonPackages; + }; - buildCfg = c: if builtins.typeOf c != "set" then builtins.readFile c else builtins.toJSON { - uwsgi = - if c.type == "normal" - then { - pythonpath = - (if c ? python2Packages - then builtins.map (x: "${x}/${pkgs.uwsgi.python2.sitePackages}") (c.python2Packages python2Pkgs) - else []) - ++ (if c ? python3Packages - then builtins.map (x: "${x}/${pkgs.uwsgi.python3.sitePackages}") (c.python3Packages python3Pkgs) - else []); - plugins = cfg.plugins; - } // removeAttrs c [ "type" "python2Packages" "python3Packages" ] - else if c.type == "emperor" - then { - emperor = if builtins.typeOf c.vassals != "set" then c.vassals - else pkgs.buildEnv { - name = "vassals"; - paths = mapAttrsToList (n: c: pkgs.writeTextDir "${n}.json" (buildCfg c)) c.vassals; - }; - } // removeAttrs c [ "type" "vassals" ] - else abort "type should be either 'normal' or 'emperor'"; - }; + json = builtins.toJSON { + uwsgi = + if c.type == "normal" + then { + inherit plugins; + } // removeAttrs c [ "type" "pythonPackages" ] + // optionalAttrs (python != null) { + pythonpath = "@PYTHONPATH@"; + env = (c.env or {}) // { + PATH = optionalString (c ? env.PATH) "${c.env.PATH}:" + "@PATH@"; + }; + } + else if c.type == "emperor" + then { + emperor = if builtins.typeOf c.vassals != "set" then c.vassals + else pkgs.buildEnv { + name = "vassals"; + paths = mapAttrsToList buildCfg c.vassals; + }; + } // removeAttrs c [ "type" "vassals" ] + else throw "`type` attribute in UWSGI configuration should be either 'normal' or 'emperor'"; + }; - uwsgi = pkgs.uwsgi.override { - plugins = cfg.plugins; - }; + in + if python == null || c.type != "normal" + then pkgs.writeTextDir "${name}.json" json + else pkgs.stdenv.mkDerivation { + name = "uwsgi-config"; + inherit json; + passAsFile = [ "json" ]; + nativeBuildInputs = [ pythonPackages.wrapPython ]; + pythonInputs = (c.pythonPackages or (self: [])) pythonPackages; + + buildCommand = '' + mkdir $out + declare -A pythonPathsSeen=() + program_PYTHONPATH= + program_PATH= + if [ -n "$pythonInputs" ]; then + for i in $pythonInputs; do + _addToPythonPath $i + done + fi + # A hack to replace "@PYTHONPATH@" with a JSON list + if [ -n "$program_PYTHONPATH" ]; then + program_PYTHONPATH="\"''${program_PYTHONPATH//:/\",\"}\"" + fi + substitute $jsonPath $out/${name}.json \ + --replace '"@PYTHONPATH@"' "[$program_PYTHONPATH]" \ + --subst-var-by PATH "$program_PATH" + ''; + }; in { @@ -71,21 +113,24 @@ in { vassals = { moin = { type = "normal"; - python2Packages = self: with self; [ moinmoin ]; + pythonPackages = self: with self; [ moinmoin ]; socket = "${config.services.uwsgi.runDir}/uwsgi.sock"; }; }; } ''; description = '' - uWSGI configuration. This awaits either a path to file or a set which will be made into one. - If given a set, it awaits an attribute <literal>type</literal> which can be either <literal>normal</literal> - or <literal>emperor</literal>. + uWSGI configuration. It awaits an attribute <literal>type</literal> inside which can be either + <literal>normal</literal> or <literal>emperor</literal>. + + For <literal>normal</literal> mode you can specify <literal>pythonPackages</literal> as a function + from libraries set into a list of libraries. <literal>pythonpath</literal> will be set accordingly. - For <literal>normal</literal> mode you can specify <literal>python2Packages</literal> and - <literal>python3Packages</literal> as functions from libraries set into lists of libraries. For <literal>emperor</literal> mode, you should use <literal>vassals</literal> attribute which should be either a set of names and configurations or a path to a directory. + + Other attributes will be used in configuration file as-is. Notice that you can redefine + <literal>plugins</literal> setting here. ''; }; @@ -118,7 +163,7 @@ in { ''; serviceConfig = { Type = "notify"; - ExecStart = "${uwsgi}/bin/uwsgi --uid ${cfg.user} --gid ${cfg.group} --json ${pkgs.writeText "uwsgi.json" (buildCfg cfg.instance)}"; + ExecStart = "${uwsgi}/bin/uwsgi --uid ${cfg.user} --gid ${cfg.group} --json ${buildCfg "server" cfg.instance}/server.json"; ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; ExecStop = "${pkgs.coreutils}/bin/kill -INT $MAINPID"; NotifyAccess = "main"; diff --git a/nixos/modules/services/x11/desktop-managers/default.nix b/nixos/modules/services/x11/desktop-managers/default.nix index 998bcd354c53..3e91450a39d2 100644 --- a/nixos/modules/services/x11/desktop-managers/default.nix +++ b/nixos/modules/services/x11/desktop-managers/default.nix @@ -64,7 +64,13 @@ in else if any (w: w.name == defaultDM) cfg.session.list then defaultDM else - throw "Default desktop manager ($(defaultDM)) not found."; + throw '' + Default desktop manager (${defaultDM}) not found. + Probably you want to change + services.xserver.desktopManager.default = "${defaultDM}"; + to one of + ${concatMapStringsSep "\n " (w: "services.xserver.desktopManager.default = \"${w.name}\";") cfg.session.list} + ''; }; }; diff --git a/nixos/modules/services/x11/desktop-managers/gnome3.nix b/nixos/modules/services/x11/desktop-managers/gnome3.nix index 55d7fd80cf97..b112fc2422a7 100644 --- a/nixos/modules/services/x11/desktop-managers/gnome3.nix +++ b/nixos/modules/services/x11/desktop-managers/gnome3.nix @@ -27,19 +27,24 @@ let nixos-gsettings-desktop-schemas = pkgs.stdenv.mkDerivation { name = "nixos-gsettings-desktop-schemas"; - buildInputs = [ pkgs.nixos-artwork ]; buildCommand = '' - mkdir -p $out/share/nixos-gsettings-schemas/nixos-gsettings-desktop-schemas - cp -rf ${gnome3.gsettings_desktop_schemas}/share/gsettings-schemas/gsettings-desktop-schemas*/glib-2.0 $out/share/nixos-gsettings-schemas/nixos-gsettings-desktop-schemas/ - chmod -R a+w $out/share/nixos-gsettings-schemas/nixos-gsettings-desktop-schemas - cat - > $out/share/nixos-gsettings-schemas/nixos-gsettings-desktop-schemas/glib-2.0/schemas/nixos-defaults.gschema.override <<- EOF + mkdir -p $out/share/gsettings-schemas/nixos-gsettings-overrides/glib-2.0/schemas + cp -rf ${gnome3.gsettings_desktop_schemas}/share/gsettings-schemas/gsettings-desktop-schemas*/glib-2.0/schemas/*.xml $out/share/gsettings-schemas/nixos-gsettings-overrides/glib-2.0/schemas + + ${concatMapStrings (pkg: "cp -rf ${pkg}/share/gsettings-schemas/*/glib-2.0/schemas/*.xml $out/share/gsettings-schemas/nixos-gsettings-overrides/glib-2.0/schemas\n") cfg.extraGSettingsOverridePackages} + + chmod -R a+w $out/share/gsettings-schemas/nixos-gsettings-overrides + cat - > $out/share/gsettings-schemas/nixos-gsettings-overrides/glib-2.0/schemas/nixos-defaults.gschema.override <<- EOF [org.gnome.desktop.background] picture-uri='${pkgs.nixos-artwork}/share/artwork/gnome/Gnome_Dark.png' [org.gnome.desktop.screensaver] picture-uri='${pkgs.nixos-artwork}/share/artwork/gnome/Gnome_Dark.png' + + ${cfg.extraGSettingsOverrides} EOF - ${pkgs.glib}/bin/glib-compile-schemas $out/share/nixos-gsettings-schemas/nixos-gsettings-desktop-schemas/glib-2.0/schemas/ + + ${pkgs.glib}/bin/glib-compile-schemas $out/share/gsettings-schemas/nixos-gsettings-overrides/glib-2.0/schemas/ ''; }; @@ -47,24 +52,38 @@ in { options = { - services.xserver.desktopManager.gnome3.enable = mkOption { - default = false; - example = true; - description = "Enable Gnome 3 desktop manager."; - }; + services.xserver.desktopManager.gnome3 = { + enable = mkOption { + default = false; + example = true; + description = "Enable Gnome 3 desktop manager."; + }; - services.xserver.desktopManager.gnome3.sessionPath = mkOption { - default = []; - example = literalExample "[ pkgs.gnome3.gpaste ]"; - description = "Additional list of packages to be added to the session search path. - Useful for gnome shell extensions or gsettings-conditionated autostart."; - apply = list: list ++ [ gnome3.gnome_shell gnome3.gnome-shell-extensions ]; - }; + sessionPath = mkOption { + default = []; + example = literalExample "[ pkgs.gnome3.gpaste ]"; + description = "Additional list of packages to be added to the session search path. + Useful for gnome shell extensions or gsettings-conditionated autostart."; + apply = list: list ++ [ gnome3.gnome_shell gnome3.gnome-shell-extensions ]; + }; + + extraGSettingsOverrides = mkOption { + default = ""; + type = types.lines; + description = "Additional gsettings overrides."; + }; + + extraGSettingsOverridePackages = mkOption { + default = []; + type = types.listOf types.path; + description = "List of packages for which gsettings are overridden."; + }; + }; environment.gnome3.packageSet = mkOption { type = types.nullOr types.package; default = null; - example = literalExample "pkgs.gnome3_16"; + example = literalExample "pkgs.gnome3_18"; description = "Which GNOME 3 package set to use."; apply = p: if p == null then pkgs.gnome3 else p; }; @@ -130,7 +149,7 @@ in { export XDG_DATA_DIRS=$XDG_DATA_DIRS''${XDG_DATA_DIRS:+:}${mimeAppsList}/share # Override gsettings-desktop-schema - export XDG_DATA_DIRS=${nixos-gsettings-desktop-schemas}/share/nixos-gsettings-schemas/nixos-gsettings-desktop-schemas''${XDG_DATA_DIRS:+:}$XDG_DATA_DIRS + export XDG_DATA_DIRS=${nixos-gsettings-desktop-schemas}/share/gsettings-schemas/nixos-gsettings-overrides''${XDG_DATA_DIRS:+:}$XDG_DATA_DIRS # Let nautilus find extensions export NAUTILUS_EXTENSION_DIR=${config.system.path}/lib/nautilus/extensions-3.0/ diff --git a/nixos/modules/services/x11/desktop-managers/kde4.nix b/nixos/modules/services/x11/desktop-managers/kde4.nix index 29cca248cde3..80e408be4923 100644 --- a/nixos/modules/services/x11/desktop-managers/kde4.nix +++ b/nixos/modules/services/x11/desktop-managers/kde4.nix @@ -108,6 +108,12 @@ in sed -e '/nix\\store\|nix\/store/ d' -i $HOME/.config/Trolltech.conf fi + # Load PulseAudio module for routing support. + # See http://colin.guthr.ie/2009/10/so-how-does-the-kde-pulseaudio-support-work-anyway/ + ${optionalString config.hardware.pulseaudio.enable '' + ${config.hardware.pulseaudio.package}/bin/pactl load-module module-device-manager "do_routing=1" + ''} + # Start KDE. exec ${kde_workspace}/bin/startkde ''; diff --git a/nixos/modules/services/x11/desktop-managers/kde5.nix b/nixos/modules/services/x11/desktop-managers/kde5.nix index e8c768e41fad..2dc03b7fe637 100644 --- a/nixos/modules/services/x11/desktop-managers/kde5.nix +++ b/nixos/modules/services/x11/desktop-managers/kde5.nix @@ -55,12 +55,20 @@ in services.xserver.desktopManager.session = singleton { name = "kde5"; bgSupport = true; - start = ''exec ${kde5.plasma-workspace}/bin/startkde;''; + start = '' + # Load PulseAudio module for routing support. + # See http://colin.guthr.ie/2009/10/so-how-does-the-kde-pulseaudio-support-work-anyway/ + ${optionalString config.hardware.pulseaudio.enable '' + ${config.hardware.pulseaudio.package}/bin/pactl load-module module-device-manager "do_routing=1" + ''} + + exec startkde + ''; }; security.setuidOwners = singleton { program = "kcheckpass"; - source = "${kde5.plasma-workspace}/lib/libexec/kcheckpass"; + source = "${kde5.plasma-workspace.out}/lib/libexec/kcheckpass"; owner = "root"; group = "root"; setuid = true; @@ -68,8 +76,6 @@ in environment.systemPackages = [ - pkgs.qt4 # qtconfig is the only way to set Qt 4 theme - kde5.frameworkintegration kde5.kinit @@ -95,16 +101,12 @@ in kde5.plasma-workspace kde5.plasma-workspace-wallpapers - kde5.ark kde5.dolphin kde5.dolphin-plugins kde5.ffmpegthumbs - kde5.gwenview - kde5.kate kde5.kdegraphics-thumbnailers kde5.kio-extras kde5.konsole - kde5.okular kde5.print-manager # Oxygen icons moved to KDE Frameworks 5.16 and later. @@ -168,12 +170,12 @@ in # Enable GTK applications to load SVG icons environment.variables = mkIf (lib.hasAttr "breeze-icons" kde5) { - GDK_PIXBUF_MODULE_FILE = "${pkgs.librsvg}/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache"; + GDK_PIXBUF_MODULE_FILE = "${pkgs.librsvg.out}/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache"; }; fonts.fonts = [ (kde5.oxygen-fonts or pkgs.noto-fonts) ]; - programs.ssh.askPassword = "${kde5.ksshaskpass}/bin/ksshaskpass"; + programs.ssh.askPassword = "${kde5.ksshaskpass.out}/bin/ksshaskpass"; # Enable helpful DBus services. services.udisks2.enable = true; diff --git a/nixos/modules/services/x11/display-managers/default.nix b/nixos/modules/services/x11/display-managers/default.nix index 3aa09193a66e..da7be748d8b2 100644 --- a/nixos/modules/services/x11/display-managers/default.nix +++ b/nixos/modules/services/x11/display-managers/default.nix @@ -16,13 +16,6 @@ let cfg = config.services.xserver; xorg = pkgs.xorg; - vaapiDrivers = pkgs.buildEnv { - name = "vaapi-drivers"; - paths = cfg.vaapiDrivers; - # We only want /lib/dri, but with a single input path, we need "/" for it to work - pathsToLink = [ "/" ]; - }; - fontconfig = config.fonts.fontconfig; xresourcesXft = pkgs.writeText "Xresources-Xft" '' ${optionalString (fontconfig.dpi != 0) ''Xft.dpi: ${toString fontconfig.dpi}''} @@ -56,18 +49,6 @@ let fi ''} - ${optionalString cfg.displayManager.desktopManagerHandlesLidAndPower '' - # Stop systemd from handling the power button and lid switch, - # since presumably the desktop environment will handle these. - if [ -z "$_INHIBITION_LOCK_TAKEN" ]; then - export _INHIBITION_LOCK_TAKEN=1 - if ! ${config.systemd.package}/bin/loginctl show-session $XDG_SESSION_ID | grep -q '^RemoteHost='; then - exec ${config.systemd.package}/bin/systemd-inhibit --what=handle-lid-switch:handle-power-key --why="Desktop environment handles power events" "$0" "$sessionType" - fi - fi - - ''} - ${optionalString cfg.startGnuPGAgent '' if test -z "$SSH_AUTH_SOCK"; then # Restart this script as a child of the GnuPG agent. @@ -90,9 +71,6 @@ let # Publish access credentials in the root window. ${config.hardware.pulseaudio.package.out}/bin/pactl load-module module-x11-publish "display=$DISPLAY" - - # Keep track of devices. Mostly useful for Phonon/KDE. - ${config.hardware.pulseaudio.package.out}/bin/pactl load-module module-device-manager "do_routing=1" ''} # Tell systemd about our $DISPLAY. This is needed by the @@ -107,8 +85,6 @@ let ${xorg.xrdb}/bin/xrdb -merge ~/.Xdefaults fi - export LIBVA_DRIVERS_PATH=${vaapiDrivers}/lib/dri - # Speed up application start by 50-150ms according to # http://kdemonkey.blogspot.nl/2008/04/magic-trick.html rm -rf $HOME/.compose-cache @@ -231,17 +207,6 @@ in ''; }; - desktopManagerHandlesLidAndPower = mkOption { - type = types.bool; - default = false; - description = '' - Whether the display manager should prevent systemd from handling - lid and power events. This is normally handled by the desktop - environment's power manager. Turn this off when using a minimal - X11 setup without a full power manager. - ''; - }; - session = mkOption { default = []; example = literalExample @@ -321,9 +286,11 @@ in }; config = { - services.xserver.displayManager.xserverBin = "${xorg.xorgserver.out}/bin/X"; - }; + imports = [ + (mkRemovedOptionModule [ "services" "xserver" "displayManager" "desktopManagerHandlesLidAndPower" ]) + ]; + } diff --git a/nixos/modules/services/x11/display-managers/lightdm.nix b/nixos/modules/services/x11/display-managers/lightdm.nix index 9460395f86d6..ac7db3d9adc4 100644 --- a/nixos/modules/services/x11/display-managers/lightdm.nix +++ b/nixos/modules/services/x11/display-managers/lightdm.nix @@ -86,7 +86,8 @@ in }; background = mkOption { - type = types.path; + type = types.str; + default = "${pkgs.nixos-artwork}/share/artwork/gnome/Gnome_Dark.png"; description = '' The background image or color to use. ''; @@ -152,9 +153,6 @@ in }; users.extraGroups.lightdm.gid = config.ids.gids.lightdm; - - services.xserver.displayManager.lightdm.background = mkDefault "${pkgs.nixos-artwork}/share/artwork/gnome/Gnome_Dark.png"; - services.xserver.tty = null; # We might start multiple X servers so let the tty increment themselves.. services.xserver.display = null; # We specify our own display (and logfile) in xserver-wrapper up there }; diff --git a/nixos/modules/services/x11/display-managers/sddm.nix b/nixos/modules/services/x11/display-managers/sddm.nix index 6b344822977f..7a17a222bcab 100644 --- a/nixos/modules/services/x11/display-managers/sddm.nix +++ b/nixos/modules/services/x11/display-managers/sddm.nix @@ -31,6 +31,9 @@ let [General] HaltCommand=${pkgs.systemd}/bin/systemctl poweroff RebootCommand=${pkgs.systemd}/bin/systemctl reboot + ${optionalString cfg.autoNumlock '' + Numlock=on + ''} [Theme] Current=${cfg.theme} @@ -110,6 +113,14 @@ in ''; }; + autoNumlock = mkOption { + type = types.bool; + default = false; + description = '' + Enable numlock at login. + ''; + }; + setupScript = mkOption { type = types.str; default = ""; diff --git a/nixos/modules/services/x11/hardware/libinput.nix b/nixos/modules/services/x11/hardware/libinput.nix new file mode 100644 index 000000000000..12cc1e7e6460 --- /dev/null +++ b/nixos/modules/services/x11/hardware/libinput.nix @@ -0,0 +1,230 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let cfg = config.services.xserver.libinput; + xorgBool = v: if v then "on" else "off"; +in { + + options = { + + services.xserver.libinput = { + + enable = mkEnableOption "libinput"; + + dev = mkOption { + type = types.nullOr types.str; + default = null; + example = "/dev/input/event0"; + description = + '' + Path for touchpad device. Set to null to apply to any + auto-detected touchpad. + ''; + }; + + accelProfile = mkOption { + type = types.enum [ "flat" "adaptive" ]; + default = "flat"; + example = "adaptive"; + description = + '' + Sets the pointer acceleration profile to the given profile. Permitted values are adaptive, flat. + Not all devices support this option or all profiles. If a profile is unsupported, the default profile + for this is used. For a description on the profiles and their behavior, see the libinput documentation. + ''; + }; + + accelSpeed = mkOption { + type = types.nullOr types.string; + default = null; + description = "Cursor acceleration (how fast speed increases from minSpeed to maxSpeed)."; + }; + + buttonMapping = mkOption { + type = types.nullOr types.string; + default = null; + description = + '' + Sets the logical button mapping for this device, see XSetPointerMapping(3). The string must + be a space-separated list of button mappings in the order of the logical buttons on the + device, starting with button 1. The default mapping is "1 2 3 ... 32". A mapping of 0 deac‐ + tivates the button. Multiple buttons can have the same mapping. Invalid mapping strings are + discarded and the default mapping is used for all buttons. Buttons not specified in the + user's mapping use the default mapping. See section BUTTON MAPPING for more details. + ''; + }; + + calibrationMatrix = mkOption { + type = types.nullOr types.string; + default = null; + description = + '' + A string of 9 space-separated floating point numbers. Sets the calibration matrix to the + 3x3 matrix where the first row is (abc), the second row is (def) and the third row is (ghi). + ''; + }; + + clickMethod = mkOption { + type = types.nullOr (types.enum [ "none" "buttonareas" "clickfinger" ]); + default = null; + example = "none"; + description = + '' + Enables a click method. Permitted values are none, buttonareas, clickfinger. + Not all devices support all methods, if an option is unsupported, + the default click method for this device is used. + ''; + }; + + leftHanded = mkOption { + type = types.bool; + default = false; + example = true; + description = "Enables left-handed button orientation, i.e. swapping left and right buttons."; + }; + + middleEmulation = mkOption { + type = types.bool; + default = true; + example = false; + description = + '' + Enables middle button emulation. When enabled, pressing the left and right buttons + simultaneously produces a middle mouse button click. + ''; + }; + + naturalScrolling = mkOption { + type = types.bool; + default = false; + example = true; + description = "Enables or disables natural scrolling behavior."; + }; + + scrollButton = mkOption { + type = types.nullOr types.int; + default = null; + example = 1; + description = + '' + Designates a button as scroll button. If the ScrollMethod is button and the button is logically + held down, x/y axis movement is converted into scroll events. + ''; + }; + + scrollMethod = mkOption { + type = types.enum [ "twofinger" "edge" "none" ]; + default = "twofinger"; + example = "edge"; + description = + '' + Specify the scrolling method. + ''; + }; + + horizontalScrolling = mkOption { + type = types.bool; + default = true; + example = false; + description = + '' + Disables horizontal scrolling. When disabled, this driver will discard any horizontal scroll + events from libinput. Note that this does not disable horizontal scrolling, it merely + discards the horizontal axis from any scroll events. + ''; + }; + + sendEventsMode = mkOption { + type = types.enum [ "disabled" "enabled" "disabled-on-external-mouse" ]; + default = "enabled"; + example = "disabled"; + description = + '' + Sets the send events mode to disabled, enabled, or "disable when an external mouse is connected". + ''; + }; + + tapping = mkOption { + type = types.bool; + default = true; + example = false; + description = + '' + Enables or disables tap-to-click behavior. + ''; + }; + + tappingDragLock = mkOption { + type = types.bool; + default = true; + example = false; + description = + '' + Enables or disables drag lock during tapping behavior. When enabled, a finger up during tap- + and-drag will not immediately release the button. If the finger is set down again within the + timeout, the draging process continues. + ''; + }; + + disableWhileTyping = mkOption { + type = types.bool; + default = true; + example = false; + description = + '' + Disable input method while typing. + ''; + }; + + additionalOptions = mkOption { + type = types.str; + default = ""; + example = + '' + Option "DragLockButtons" "L1 B1 L2 B2" + ''; + description = "Additional options for libinput touchpad driver."; + }; + + }; + + }; + + + config = mkIf cfg.enable { + + services.xserver.modules = [ pkgs.xorg.xf86inputlibinput ]; + + environment.systemPackages = [ pkgs.xorg.xf86inputlibinput ]; + + services.xserver.config = + '' + # Automatically enable the libinput driver for all touchpads. + Section "InputClass" + Identifier "libinputConfiguration" + MatchIsTouchpad "on" + ${optionalString (cfg.dev != null) ''MatchDevicePath "${cfg.dev}"''} + Driver "libinput" + Option "AccelProfile" "${cfg.accelProfile}" + ${optionalString (cfg.accelSpeed != null) ''Option "AccelSpeed" "${cfg.accelSpeed}"''} + ${optionalString (cfg.buttonMapping != null) ''Option "ButtonMapping" "${cfg.buttonMapping}"''} + ${optionalString (cfg.calibrationMatrix != null) ''Option "CalibrationMatrix" "${cfg.calibrationMatrix}"''} + ${optionalString (cfg.clickMethod != null) ''Option "ClickMethod" "${cfg.clickMethod}"''} + Option "LeftHanded" "${xorgBool cfg.leftHanded}" + Option "MiddleEmulation" "${xorgBool cfg.middleEmulation}" + Option "NaturalScrolling" "${xorgBool cfg.naturalScrolling}" + ${optionalString (cfg.scrollButton != null) ''Option "ScrollButton" "${cfg.scrollButton}"''} + Option "ScrollMethod" "${cfg.scrollMethod}" + Option "HorizontalScrolling" "${xorgBool cfg.horizontalScrolling}" + Option "SendEventsMode" "${cfg.sendEventsMode}" + Option "Tapping" "${xorgBool cfg.tapping}" + Option "TappingDragLock" "${xorgBool cfg.tappingDragLock}" + Option "DisableWhileTyping" "${xorgBool cfg.disableWhileTyping}" + ${cfg.additionalOptions} + EndSection + ''; + + }; + +} diff --git a/nixos/modules/services/x11/hardware/synaptics.nix b/nixos/modules/services/x11/hardware/synaptics.nix index e50ed08a218a..2981e7545e81 100644 --- a/nixos/modules/services/x11/hardware/synaptics.nix +++ b/nixos/modules/services/x11/hardware/synaptics.nix @@ -62,6 +62,13 @@ in { description = "Cursor speed factor for highest-speed finger motion."; }; + scrollDelta = mkOption { + type = types.nullOr types.int; + default = null; + example = 75; + description = "Move distance of the finger for a scroll event."; + }; + twoFingerScroll = mkOption { type = types.bool; default = false; @@ -122,6 +129,20 @@ in { description = "Whether to enable palm detection (hardware support required)"; }; + palmMinWidth = mkOption { + type = types.nullOr types.int; + default = null; + example = 5; + description = "Minimum finger width at which touch is considered a palm"; + }; + + palmMinZ = mkOption { + type = types.nullOr types.int; + default = null; + example = 20; + description = "Minimum finger pressure at which touch is considered a palm"; + }; + horizontalScroll = mkOption { type = types.bool; default = true; @@ -174,8 +195,12 @@ in { Option "HorizTwoFingerScroll" "${if cfg.horizTwoFingerScroll then "1" else "0"}" Option "VertEdgeScroll" "${if cfg.vertEdgeScroll then "1" else "0"}" Option "HorizEdgeScroll" "${if cfg.horizEdgeScroll then "1" else "0"}" - ${if cfg.palmDetect then ''Option "PalmDetect" "1"'' else ""} - ${if cfg.horizontalScroll then "" else ''Option "HorizScrollDelta" "0"''} + ${optionalString cfg.palmDetect ''Option "PalmDetect" "1"''} + ${optionalString (cfg.palmMinWidth != null) ''Option "PalmMinWidth" "${toString cfg.palmMinWidth}"''} + ${optionalString (cfg.palmMinZ != null) ''Option "PalmMinZ" "${toString cfg.palmMinZ}"''} + ${optionalString (cfg.scrollDelta != null) ''Option "VertScrollDelta" "${toString cfg.scrollDelta}"''} + ${if !cfg.horizontalScroll then ''Option "HorizScrollDelta" "0"'' + else (optionalString (cfg.scrollDelta != null) ''Option "HorizScrollDelta" "${toString cfg.scrollDelta}"'')} ${cfg.additionalOptions} EndSection ''; diff --git a/nixos/modules/services/x11/window-managers/bspwm.nix b/nixos/modules/services/x11/window-managers/bspwm.nix index 8b4e91d25aa4..271b1b6cf5da 100644 --- a/nixos/modules/services/x11/window-managers/bspwm.nix +++ b/nixos/modules/services/x11/window-managers/bspwm.nix @@ -8,16 +8,39 @@ in { options = { - services.xserver.windowManager.bspwm.enable = mkEnableOption "bspwm"; + services.xserver.windowManager.bspwm = { + enable = mkEnableOption "bspwm"; + startThroughSession = mkOption { + type = with types; bool; + default = false; + description = " + Start the window manager through the script defined in + sessionScript. Defaults to the the bspwm-session script + provided by bspwm + "; + }; + sessionScript = mkOption { + default = "${pkgs.bspwm}/bin/bspwm-session"; + defaultText = "(pkgs.bspwm)/bin/bspwm-session"; + description = " + The start-session script to use. Defaults to the + provided bspwm-session script from the bspwm package. + + Does nothing unless `bspwm.startThroughSession` is enabled + "; + }; + }; }; config = mkIf cfg.enable { services.xserver.windowManager.session = singleton { name = "bspwm"; - start = " - ${pkgs.sxhkd}/bin/sxhkd & - ${pkgs.bspwm}/bin/bspwm - "; + start = if cfg.startThroughSession + then cfg.sessionScript + else '' + SXHKD_SHELL=/bin/sh ${pkgs.sxhkd}/bin/sxhkd -f 100 & + ${pkgs.bspwm}/bin/bspwm + ''; }; environment.systemPackages = [ pkgs.bspwm ]; }; diff --git a/nixos/modules/services/x11/window-managers/default.nix b/nixos/modules/services/x11/window-managers/default.nix index 37d3348b8a32..26dfbb1f4e18 100644 --- a/nixos/modules/services/x11/window-managers/default.nix +++ b/nixos/modules/services/x11/window-managers/default.nix @@ -13,6 +13,7 @@ in ./clfswm.nix ./compiz.nix ./dwm.nix + ./exwm.nix ./fluxbox.nix ./herbstluftwm.nix ./i3.nix diff --git a/nixos/modules/services/x11/window-managers/exwm.nix b/nixos/modules/services/x11/window-managers/exwm.nix new file mode 100644 index 000000000000..dbbd8a125d66 --- /dev/null +++ b/nixos/modules/services/x11/window-managers/exwm.nix @@ -0,0 +1,55 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.xserver.windowManager.exwm; + loadScript = pkgs.writeText "emacs-exwm-load" '' + (require 'exwm) + ${optionalString cfg.enableDefaultConfig '' + (require 'exwm-config) + (exwm-config-default) + ''} + ''; + packages = epkgs: cfg.extraPackages epkgs ++ [ epkgs.exwm ]; + exwm-emacs = pkgs.emacsWithPackages packages; +in + +{ + options = { + services.xserver.windowManager.exwm = { + enable = mkEnableOption "exwm"; + enableDefaultConfig = mkOption { + default = true; + example = false; + type = lib.types.bool; + description = "Enable an uncustomised exwm configuration."; + }; + extraPackages = mkOption { + default = self: []; + example = literalExample '' + epkgs: [ + epkgs.emms + epkgs.magit + epkgs.proofgeneral + ] + ''; + description = '' + Extra packages available to Emacs. The value must be a + function which receives the attrset defined in + <varname>emacsPackages</varname> as the sole argument. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + services.xserver.windowManager.session = singleton { + name = "exwm"; + start = '' + ${exwm-emacs}/bin/emacs -l ${loadScript} + ''; + }; + environment.systemPackages = [ exwm-emacs ]; + }; +} diff --git a/nixos/modules/services/x11/window-managers/i3.nix b/nixos/modules/services/x11/window-managers/i3.nix index 0d5816e363d6..2e2e10cc33b0 100644 --- a/nixos/modules/services/x11/window-managers/i3.nix +++ b/nixos/modules/services/x11/window-managers/i3.nix @@ -34,6 +34,6 @@ in ''; }]; }; - environment.systemPackages = [ pkgs.i3 ]; + environment.systemPackages = with pkgs; [ i3 ]; }; } diff --git a/nixos/modules/services/x11/window-managers/metacity.nix b/nixos/modules/services/x11/window-managers/metacity.nix index 3e5229be634f..2957ad91be45 100644 --- a/nixos/modules/services/x11/window-managers/metacity.nix +++ b/nixos/modules/services/x11/window-managers/metacity.nix @@ -20,7 +20,7 @@ in services.xserver.windowManager.session = singleton { name = "metacity"; start = '' - env LD_LIBRARY_PATH=${xorg.libX11}/lib:${xorg.libXext}/lib:/usr/lib/ + env LD_LIBRARY_PATH=${xorg.libX11.out}/lib:${xorg.libXext.out}/lib:/usr/lib/ # !!! Hack: load the schemas for Metacity. GCONF_CONFIG_SOURCE=xml::~/.gconf ${gnome.GConf}/bin/gconftool-2 \ --makefile-install-rule ${gnome.metacity}/etc/gconf/schemas/*.schemas # */ diff --git a/nixos/modules/services/x11/window-managers/openbox.nix b/nixos/modules/services/x11/window-managers/openbox.nix index 091b533b28be..07ef77151e95 100644 --- a/nixos/modules/services/x11/window-managers/openbox.nix +++ b/nixos/modules/services/x11/window-managers/openbox.nix @@ -8,7 +8,7 @@ in { options = { - services.xserver.windowManager.openbox.enable = mkEnableOption "oroborus"; + services.xserver.windowManager.openbox.enable = mkEnableOption "openbox"; }; config = mkIf cfg.enable { diff --git a/nixos/modules/services/x11/xserver.nix b/nixos/modules/services/x11/xserver.nix index 86f0641b0e37..60b6a97416ab 100644 --- a/nixos/modules/services/x11/xserver.nix +++ b/nixos/modules/services/x11/xserver.nix @@ -156,13 +156,16 @@ in inputClassSections = mkOption { type = types.listOf types.lines; default = []; - example = [ '' - Identifier "Trackpoint Wheel Emulation" - MatchProduct "ThinkPad USB Keyboard with TrackPoint" - Option "EmulateWheel" "true - Option "EmulateWheelButton" "2" - Option "Emulate3Buttons" "false" - '' ]; + example = literalExample '' + [ ''' + Identifier "Trackpoint Wheel Emulation" + MatchProduct "ThinkPad USB Keyboard with TrackPoint" + Option "EmulateWheel" "true + Option "EmulateWheelButton" "2" + Option "Emulate3Buttons" "false" + ''' + ] + ''; description = "Content of additional InputClass sections of the X server configuration file."; }; @@ -216,15 +219,6 @@ in ''; }; - vaapiDrivers = mkOption { - type = types.listOf types.path; - default = [ ]; - example = literalExample "[ pkgs.vaapiIntel pkgs.vaapiVdpau ]"; - description = '' - Packages providing libva acceleration drivers. - ''; - }; - startGnuPGAgent = mkOption { type = types.bool; default = false; diff --git a/nixos/modules/system/activation/activation-script.nix b/nixos/modules/system/activation/activation-script.nix index dd2ce1894ae6..9d61d64f7553 100644 --- a/nixos/modules/system/activation/activation-script.nix +++ b/nixos/modules/system/activation/activation-script.nix @@ -31,18 +31,19 @@ in system.activationScripts = mkOption { default = {}; - example = { - stdio = { - text = '' - # Needed by some programs. - ln -sfn /proc/self/fd /dev/fd - ln -sfn /proc/self/fd/0 /dev/stdin - ln -sfn /proc/self/fd/1 /dev/stdout - ln -sfn /proc/self/fd/2 /dev/stderr - ''; - deps = []; - }; - }; + example = literalExample '' + { stdio = { + text = ''' + # Needed by some programs. + ln -sfn /proc/self/fd /dev/fd + ln -sfn /proc/self/fd/0 /dev/stdin + ln -sfn /proc/self/fd/1 /dev/stdout + ln -sfn /proc/self/fd/2 /dev/stderr + '''; + deps = []; + }; + } + ''; description = '' A set of shell script fragments that are executed when a NixOS @@ -95,6 +96,18 @@ in }; + environment.usrbinenv = mkOption { + default = "${pkgs.coreutils}/bin/env"; + example = literalExample '' + "''${pkgs.busybox}/bin/env" + ''; + type = types.nullOr types.path; + visible = false; + description = '' + The env(1) executable that is linked system-wide to + <literal>/usr/bin/env</literal>. + ''; + }; }; @@ -129,11 +142,15 @@ in mkdir -m 0555 -p /var/empty ''; - system.activationScripts.usrbinenv = - '' + system.activationScripts.usrbinenv = if config.environment.usrbinenv != null + then '' mkdir -m 0755 -p /usr/bin - ln -sfn ${pkgs.coreutils}/bin/env /usr/bin/.env.tmp + ln -sfn ${config.environment.usrbinenv} /usr/bin/.env.tmp mv /usr/bin/.env.tmp /usr/bin/env # atomically replace /usr/bin/env + '' + else '' + rm -f /usr/bin/env + rmdir --ignore-fail-on-non-empty /usr/bin /usr ''; system.activationScripts.tmpfs = diff --git a/nixos/modules/system/activation/switch-to-configuration.pl b/nixos/modules/system/activation/switch-to-configuration.pl index 655fbab2a843..093b7f1ff225 100644 --- a/nixos/modules/system/activation/switch-to-configuration.pl +++ b/nixos/modules/system/activation/switch-to-configuration.pl @@ -261,12 +261,12 @@ while (my ($unit, $state) = each %{$activePrev}) { sub pathToUnitName { my ($path) = @_; - die unless substr($path, 0, 1) eq "/"; - return "-" if $path eq "/"; - $path = substr($path, 1); - $path =~ s/\//-/g; - # FIXME: handle - and unprintable characters. - return $path; + open my $cmd, "-|", "systemd-escape", "--suffix=mount", "-p", $path + or die "Unable to escape $path!\n"; + my $escaped = join "", <$cmd>; + chomp $escaped; + close $cmd or die; + return $escaped; } sub unique { @@ -290,7 +290,7 @@ my ($newFss, $newSwaps) = parseFstab "$out/etc/fstab"; foreach my $mountPoint (keys %$prevFss) { my $prev = $prevFss->{$mountPoint}; my $new = $newFss->{$mountPoint}; - my $unit = pathToUnitName($mountPoint) . ".mount"; + my $unit = pathToUnitName($mountPoint); if (!defined $new) { # Filesystem entry disappeared, so unmount it. $unitsToStop{$unit} = 1; @@ -323,7 +323,7 @@ foreach my $device (keys %$prevSwaps) { # Should we have systemd re-exec itself? -my $prevSystemd = abs_path("/proc/1/exe") or die; +my $prevSystemd = abs_path("/proc/1/exe") // "/unknown"; my $newSystemd = abs_path("@systemd@/lib/systemd/systemd") or die; my $restartSystemd = $prevSystemd ne $newSystemd; diff --git a/nixos/modules/system/activation/top-level.nix b/nixos/modules/system/activation/top-level.nix index d66580b7b9be..2d1b0ffb54ce 100644 --- a/nixos/modules/system/activation/top-level.nix +++ b/nixos/modules/system/activation/top-level.nix @@ -178,9 +178,10 @@ in default = false; description = '' If enabled, copies the NixOS configuration file - <literal>$NIXOS_CONFIG</literal> (usually - <filename>/etc/nixos/configuration.nix</filename>) - to the system store path. + (usually <filename>/etc/nixos/configuration.nix</filename>) + and links it from the resulting system + (getting to <filename>/run/current-system/configuration.nix</filename>). + Note that only this single file is copied, even if it imports others. ''; }; @@ -238,7 +239,9 @@ in system.extraSystemBuilderCmds = optionalString config.system.copySystemConfiguration - "cp ${maybeEnv "NIXOS_CONFIG" "/etc/nixos/configuration.nix"} $out"; + ''ln -s '${import ../../../lib/from-env.nix "NIXOS_CONFIG" <nixos-config>}' \ + "$out/configuration.nix" + ''; system.build.toplevel = system; diff --git a/nixos/modules/system/boot/initrd-network.nix b/nixos/modules/system/boot/initrd-network.nix index 6c6e2fafad43..6e226c190609 100644 --- a/nixos/modules/system/boot/initrd-network.nix +++ b/nixos/modules/system/boot/initrd-network.nix @@ -3,9 +3,28 @@ with lib; let + cfg = config.boot.initrd.network; + udhcpcScript = pkgs.writeScript "udhcp-script" + '' + #! /bin/sh + if [ "$1" = bound ]; then + ip address add "$ip/$mask" dev "$interface" + if [ -n "$router" ]; then + ip route add default via "$router" dev "$interface" + fi + if [ -n "$dns" ]; then + rm -f /etc/resolv.conf + for i in $dns; do + echo "nameserver $dns" >> /etc/resolv.conf + done + fi + fi + ''; + in + { options = { @@ -14,82 +33,25 @@ in type = types.bool; default = false; description = '' - Add network connectivity support to initrd. - - Network options are configured via <literal>ip</literal> kernel - option, according to the kernel documentation. - ''; - }; - - boot.initrd.network.ssh.enable = mkOption { - type = types.bool; - default = false; - description = '' - Start SSH service during initrd boot. It can be used to debug failing - boot on a remote server, enter pasphrase for an encrypted partition etc. - Service is killed when stage-1 boot is finished. - ''; - }; - - boot.initrd.network.ssh.port = mkOption { - type = types.int; - default = 22; - description = '' - Port on which SSH initrd service should listen. - ''; - }; - - boot.initrd.network.ssh.shell = mkOption { - type = types.str; - default = "/bin/ash"; - description = '' - Login shell of the remote user. Can be used to limit actions user can do. + Add network connectivity support to initrd. The network may be + configured using the <literal>ip</literal> kernel parameter, + as described in <link + xlink:href="https://www.kernel.org/doc/Documentation/filesystems/nfs/nfsroot.txt">the + kernel documentation</link>. Otherwise, if + <option>networking.useDHCP</option> is enabled, an IP address + is acquired using DHCP. ''; }; - boot.initrd.network.ssh.hostRSAKey = mkOption { - type = types.nullOr types.path; - default = null; + boot.initrd.network.postCommands = mkOption { + default = ""; + type = types.lines; description = '' - RSA SSH private key file in the Dropbear format. - - WARNING: This key is contained insecurely in the global Nix store. Do NOT - use your regular SSH host private keys for this purpose or you'll expose - them to regular users! - ''; - }; - - boot.initrd.network.ssh.hostDSSKey = mkOption { - type = types.nullOr types.path; - default = null; - description = '' - DSS SSH private key file in the Dropbear format. - - WARNING: This key is contained insecurely in the global Nix store. Do NOT - use your regular SSH host private keys for this purpose or you'll expose - them to regular users! - ''; - }; - - boot.initrd.network.ssh.hostECDSAKey = mkOption { - type = types.nullOr types.path; - default = null; - description = '' - ECDSA SSH private key file in the Dropbear format. - - WARNING: This key is contained insecurely in the global Nix store. Do NOT - use your regular SSH host private keys for this purpose or you'll expose - them to regular users! + Shell commands to be executed after stage 1 of the + boot has initialised the network. ''; }; - boot.initrd.network.ssh.authorizedKeys = mkOption { - type = types.listOf types.str; - default = config.users.extraUsers.root.openssh.authorizedKeys.keys; - description = '' - Authorized keys for the root user on initrd. - ''; - }; }; @@ -99,51 +61,43 @@ in boot.initrd.extraUtilsCommands = '' copy_bin_and_libs ${pkgs.mkinitcpio-nfs-utils}/bin/ipconfig - '' + optionalString cfg.ssh.enable '' - copy_bin_and_libs ${pkgs.dropbear}/bin/dropbear - - cp -pv ${pkgs.glibc}/lib/libnss_files.so.* $out/lib ''; - boot.initrd.extraUtilsCommandsTest = optionalString cfg.ssh.enable '' - $out/bin/dropbear -V - ''; - - boot.initrd.postEarlyDeviceCommands = '' - # Search for interface definitions in command line - for o in $(cat /proc/cmdline); do - case $o in - ip=*) - ipconfig $o && hasNetwork=1 - ;; - esac - done - '' + optionalString cfg.ssh.enable '' - if [ -n "$hasNetwork" ]; then - mkdir /dev/pts - mount -t devpts devpts /dev/pts - - mkdir -p /etc - echo 'root:x:0:0:root:/root:${cfg.ssh.shell}' > /etc/passwd - echo '${cfg.ssh.shell}' > /etc/shells - echo 'passwd: files' > /etc/nsswitch.conf - - mkdir -p /var/log - touch /var/log/lastlog - - mkdir -p /etc/dropbear - ${optionalString (cfg.ssh.hostRSAKey != null) "ln -s ${cfg.ssh.hostRSAKey} /etc/dropbear/dropbear_rsa_host_key"} - ${optionalString (cfg.ssh.hostDSSKey != null) "ln -s ${cfg.ssh.hostDSSKey} /etc/dropbear/dropbear_dss_host_key"} - ${optionalString (cfg.ssh.hostECDSAKey != null) "ln -s ${cfg.ssh.hostECDSAKey} /etc/dropbear/dropbear_ecdsa_host_key"} - - mkdir -p /root/.ssh - ${concatStrings (map (key: '' - echo -n ${escapeShellArg key} >> /root/.ssh/authorized_keys - '') cfg.ssh.authorizedKeys)} - - dropbear -s -j -k -E -m -p ${toString cfg.ssh.port} - fi - ''; + boot.initrd.preLVMCommands = mkBefore ( + # Search for interface definitions in command line. + '' + for o in $(cat /proc/cmdline); do + case $o in + ip=*) + ipconfig $o && hasNetwork=1 + ;; + esac + done + '' + + # Otherwise, use DHCP. + + optionalString config.networking.useDHCP '' + if [ -z "$hasNetwork" ]; then + + # Bring up all interfaces. + for iface in $(cd /sys/class/net && ls); do + echo "bringing up network interface $iface..." + ip link set "$iface" up + done + + # Acquire a DHCP lease. + echo "acquiring IP address via DHCP..." + udhcpc --quit --now --script ${udhcpcScript} && hasNetwork=1 + fi + '' + + + '' + if [ -n "$hasNetwork" ]; then + echo "networking is up!" + ${cfg.postCommands} + fi + ''); }; + } diff --git a/nixos/modules/system/boot/initrd-ssh.nix b/nixos/modules/system/boot/initrd-ssh.nix new file mode 100644 index 000000000000..4cdc81541955 --- /dev/null +++ b/nixos/modules/system/boot/initrd-ssh.nix @@ -0,0 +1,124 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.boot.initrd.network.ssh; + +in + +{ + + options = { + + boot.initrd.network.ssh.enable = mkOption { + type = types.bool; + default = false; + description = '' + Start SSH service during initrd boot. It can be used to debug failing + boot on a remote server, enter pasphrase for an encrypted partition etc. + Service is killed when stage-1 boot is finished. + ''; + }; + + boot.initrd.network.ssh.port = mkOption { + type = types.int; + default = 22; + description = '' + Port on which SSH initrd service should listen. + ''; + }; + + boot.initrd.network.ssh.shell = mkOption { + type = types.str; + default = "/bin/ash"; + description = '' + Login shell of the remote user. Can be used to limit actions user can do. + ''; + }; + + boot.initrd.network.ssh.hostRSAKey = mkOption { + type = types.nullOr types.path; + default = null; + description = '' + RSA SSH private key file in the Dropbear format. + + WARNING: This key is contained insecurely in the global Nix store. Do NOT + use your regular SSH host private keys for this purpose or you'll expose + them to regular users! + ''; + }; + + boot.initrd.network.ssh.hostDSSKey = mkOption { + type = types.nullOr types.path; + default = null; + description = '' + DSS SSH private key file in the Dropbear format. + + WARNING: This key is contained insecurely in the global Nix store. Do NOT + use your regular SSH host private keys for this purpose or you'll expose + them to regular users! + ''; + }; + + boot.initrd.network.ssh.hostECDSAKey = mkOption { + type = types.nullOr types.path; + default = null; + description = '' + ECDSA SSH private key file in the Dropbear format. + + WARNING: This key is contained insecurely in the global Nix store. Do NOT + use your regular SSH host private keys for this purpose or you'll expose + them to regular users! + ''; + }; + + boot.initrd.network.ssh.authorizedKeys = mkOption { + type = types.listOf types.str; + default = config.users.extraUsers.root.openssh.authorizedKeys.keys; + description = '' + Authorized keys for the root user on initrd. + ''; + }; + + }; + + config = mkIf (config.boot.initrd.network.enable && cfg.enable) { + + boot.initrd.extraUtilsCommands = '' + copy_bin_and_libs ${pkgs.dropbear}/bin/dropbear + cp -pv ${pkgs.glibc}/lib/libnss_files.so.* $out/lib + ''; + + boot.initrd.extraUtilsCommandsTest = '' + $out/bin/dropbear -V + ''; + + boot.initrd.network.postCommands = '' + mkdir /dev/pts + mount -t devpts devpts /dev/pts + + echo '${cfg.shell}' > /etc/shells + echo 'root:x:0:0:root:/root:${cfg.shell}' > /etc/passwd + echo 'passwd: files' > /etc/nsswitch.conf + + mkdir -p /var/log + touch /var/log/lastlog + + mkdir -p /etc/dropbear + ${optionalString (cfg.hostRSAKey != null) "ln -s ${cfg.hostRSAKey} /etc/dropbear/dropbear_rsa_host_key"} + ${optionalString (cfg.hostDSSKey != null) "ln -s ${cfg.hostDSSKey} /etc/dropbear/dropbear_dss_host_key"} + ${optionalString (cfg.hostECDSAKey != null) "ln -s ${cfg.hostECDSAKey} /etc/dropbear/dropbear_ecdsa_host_key"} + + mkdir -p /root/.ssh + ${concatStrings (map (key: '' + echo -n ${escapeShellArg key} >> /root/.ssh/authorized_keys + '') cfg.authorizedKeys)} + + dropbear -s -j -k -E -m -p ${toString cfg.port} + ''; + + }; + +} diff --git a/nixos/modules/system/boot/kernel.nix b/nixos/modules/system/boot/kernel.nix index 6bc046d02611..a6bbca9b30bb 100644 --- a/nixos/modules/system/boot/kernel.nix +++ b/nixos/modules/system/boot/kernel.nix @@ -184,6 +184,9 @@ in "ide_disk" "ide_generic" + # SD cards and internal eMMC drives. + "mmc_block" + # Support USB keyboards, in case the boot fails and we only have # a USB keyboard. "uhci_hcd" diff --git a/nixos/modules/system/boot/loader/init-script/init-script-builder.sh b/nixos/modules/system/boot/loader/init-script/init-script-builder.sh index 502b3b63af2f..08d4ab14c9ca 100644 --- a/nixos/modules/system/boot/loader/init-script/init-script-builder.sh +++ b/nixos/modules/system/boot/loader/init-script/init-script-builder.sh @@ -80,8 +80,13 @@ for generation in $( | sort -n -r); do link=/nix/var/nix/profiles/system-$generation-link date=$(stat --printf="%y\n" $link | sed 's/\..*//') - kernelVersion=$(cd $(dirname $(readlink -f $link/kernel))/lib/modules && echo *) - addEntry "NixOS - Configuration $generation ($date - $kernelVersion)" $link "$generation ($date)" + if [ -d $link/kernel ]; then + kernelVersion=$(cd $(dirname $(readlink -f $link/kernel))/lib/modules && echo *) + suffix="($date - $kernelVersion)" + else + suffix="($date)" + fi + addEntry "NixOS - Configuration $generation $suffix" $link "$generation ($date)" done mv $tmpOther $targetOther diff --git a/nixos/modules/system/boot/luksroot.nix b/nixos/modules/system/boot/luksroot.nix index 59bff5472e84..77a82547031a 100644 --- a/nixos/modules/system/boot/luksroot.nix +++ b/nixos/modules/system/boot/luksroot.nix @@ -436,9 +436,9 @@ in ${optionalString luks.yubikeySupport '' copy_bin_and_libs ${pkgs.ykpers}/bin/ykchalresp copy_bin_and_libs ${pkgs.ykpers}/bin/ykinfo - copy_bin_and_libs ${pkgs.openssl}/bin/openssl + copy_bin_and_libs ${pkgs.openssl.bin}/bin/openssl - cc -O3 -I${pkgs.openssl}/include -L${pkgs.openssl}/lib ${./pbkdf2-sha512.c} -o pbkdf2-sha512 -lcrypto + cc -O3 -I${pkgs.openssl}/include -L${pkgs.openssl.out}/lib ${./pbkdf2-sha512.c} -o pbkdf2-sha512 -lcrypto strip -s pbkdf2-sha512 copy_bin_and_libs pbkdf2-sha512 diff --git a/nixos/modules/system/boot/networkd.nix b/nixos/modules/system/boot/networkd.nix index 34eea9af83b1..ab7485500261 100644 --- a/nixos/modules/system/boot/networkd.nix +++ b/nixos/modules/system/boot/networkd.nix @@ -93,11 +93,13 @@ let checkNetwork = checkUnitConfig "Network" [ (assertOnlyFields [ - "Description" "DHCP" "DHCPServer" "IPv4LL" "IPv4LLRoute" + "Description" "DHCP" "DHCPServer" "IPForward" "IPMasquerade" "IPv4LL" "IPv4LLRoute" "LLMNR" "Domains" "Bridge" "Bond" ]) (assertValueOneOf "DHCP" ["both" "none" "v4" "v6"]) (assertValueOneOf "DHCPServer" boolValues) + (assertValueOneOf "IPForward" ["yes" "no" "ipv4" "ipv6"]) + (assertValueOneOf "IPMasquerade" boolValues) (assertValueOneOf "IPv4LL" boolValues) (assertValueOneOf "IPv4LLRoute" boolValues) (assertValueOneOf "LLMNR" boolValues) @@ -129,6 +131,16 @@ let (assertValueOneOf "RequestBroadcast" boolValues) ]; + checkDhcpServer = checkUnitConfig "DHCPServer" [ + (assertOnlyFields [ + "PoolOffset" "PoolSize" "DefaultLeaseTimeSec" "MaxLeaseTimeSec" + "EmitDNS" "DNS" "EmitNTP" "NTP" "EmitTimezone" "Timezone" + ]) + (assertValueOneOf "EmitDNS" boolValues) + (assertValueOneOf "EmitNTP" boolValues) + (assertValueOneOf "EmitTimezone" boolValues) + ]; + commonNetworkOptions = { enable = mkOption { @@ -341,6 +353,18 @@ let ''; }; + dhcpServerConfig = mkOption { + default = {}; + example = { PoolOffset = 50; EmitDNS = false; }; + type = types.addCheck (types.attrsOf unitOption) checkDhcpServer; + description = '' + Each attribute in this set specifies an option in the + <literal>[DHCPServer]</literal> section of the unit. See + <citerefentry><refentrytitle>systemd.network</refentrytitle> + <manvolnum>5</manvolnum></citerefentry> for details. + ''; + }; + name = mkOption { type = types.nullOr types.str; default = null; @@ -566,6 +590,11 @@ let ${attrsToSection def.dhcpConfig} ''} + ${optionalString (def.dhcpServerConfig != { }) '' + [DHCPServer] + ${attrsToSection def.dhcpServerConfig} + + ''} ${flip concatMapStrings def.addresses (x: '' [Address] ${attrsToSection x.addressConfig} diff --git a/nixos/modules/system/boot/stage-1-init.sh b/nixos/modules/system/boot/stage-1-init.sh index 7fb368e8c44f..c0c2b6a94164 100644 --- a/nixos/modules/system/boot/stage-1-init.sh +++ b/nixos/modules/system/boot/stage-1-init.sh @@ -71,6 +71,23 @@ mount -t devtmpfs -o "size=@devSize@" devtmpfs /dev mkdir -p /run mount -t tmpfs -o "mode=0755,size=@runSize@" tmpfs /run +# Log the script output to /dev/kmsg or /run/log/stage-1-init.log. +mkdir -p /tmp +mkfifo /tmp/stage-1-init.log.fifo +logOutFd=8 && logErrFd=9 +eval "exec $logOutFd>&1 $logErrFd>&2" +if test -w /dev/kmsg; then + tee -i < /tmp/stage-1-init.log.fifo /proc/self/fd/"$logOutFd" | while read -r line; do + if test -n "$line"; then + echo "<7>stage-1-init: $line" > /dev/kmsg + fi + done & +else + mkdir -p /run/log + tee -i < /tmp/stage-1-init.log.fifo /run/log/stage-1-init.log & +fi +exec > /tmp/stage-1-init.log.fifo 2>&1 + # Process the kernel command line. export stage2Init=/init @@ -150,10 +167,6 @@ udevadm trigger --action=add udevadm settle -# Additional devices initialization. -@postEarlyDeviceCommands@ - - # Load boot-time keymap before any LVM/LUKS initialization @extraUtils@/bin/busybox loadkmap < "@busyboxKeymap@" @@ -419,6 +432,14 @@ fi # Stop udevd. udevadm control --exit +# Reset the logging file descriptors. +# Do this just before pkill, which will kill the tee process. +if test -n "@logCommands@" +then + exec 1>&$logOutFd 2>&$logErrFd + eval "exec $logOutFd>&- $logErrFd>&-" +fi + # Kill any remaining processes, just to be sure we're not taking any # with us into stage 2. But keep storage daemons like unionfs-fuse. pkill -9 -v -f '@' diff --git a/nixos/modules/system/boot/stage-1.nix b/nixos/modules/system/boot/stage-1.nix index 81418bdf1c15..3367fedc2865 100644 --- a/nixos/modules/system/boot/stage-1.nix +++ b/nixos/modules/system/boot/stage-1.nix @@ -66,10 +66,6 @@ let copy_bin_and_libs $BIN done - # Copy modprobe. - copy_bin_and_libs ${pkgs.kmod}/bin/kmod - ln -sf kmod $out/bin/modprobe - # Copy resize2fs if needed. ${optionalString (any (fs: fs.autoResize) (attrValues config.fileSystems)) '' # We need mke2fs in the initrd. @@ -161,7 +157,9 @@ let --replace /sbin/blkid ${extraUtils}/bin/blkid \ --replace ${pkgs.lvm2}/sbin ${extraUtils}/bin \ --replace /sbin/mdadm ${extraUtils}/bin/mdadm \ - --replace /bin/sh ${extraUtils}/bin/sh + --replace /bin/sh ${extraUtils}/bin/sh \ + --replace /usr/bin/readlink ${extraUtils}/bin/readlink \ + --replace /usr/bin/basename ${extraUtils}/bin/basename done # Work around a bug in QEMU, which doesn't implement the "READ @@ -203,13 +201,13 @@ let inherit (config.boot) resumeDevice devSize runSize; inherit (config.boot.initrd) checkJournalingFS - preLVMCommands preDeviceCommands postEarlyDeviceCommands postDeviceCommands postMountCommands kernelModules; + preLVMCommands preDeviceCommands postDeviceCommands postMountCommands kernelModules; resumeDevices = map (sd: if sd ? device then sd.device else "/dev/disk/by-label/${sd.label}") (filter (sd: (sd ? label || hasPrefix "/dev/" sd.device) && !sd.randomEncryption) config.swapDevices); fsInfo = - let f = fs: [ fs.mountPoint (if fs.device != null then fs.device else "/dev/disk/by-label/${fs.label}") fs.fsType fs.options ]; + let f = fs: [ fs.mountPoint (if fs.device != null then fs.device else "/dev/disk/by-label/${fs.label}") fs.fsType (builtins.concatStringsSep "," fs.options) ]; in pkgs.writeText "initrd-fsinfo" (concatStringsSep "\n" (concatMap f fileSystems)); setHostId = optionalString (config.networking.hostId != null) '' @@ -322,14 +320,6 @@ in ''; }; - boot.initrd.postEarlyDeviceCommands = mkOption { - default = ""; - type = types.lines; - description = '' - Shell commands to be executed early after creation of device nodes. - ''; - }; - boot.initrd.postMountCommands = mkOption { default = ""; type = types.lines; diff --git a/nixos/modules/system/boot/stage-2-init.sh b/nixos/modules/system/boot/stage-2-init.sh index a73070ab332f..1b5b22c2005a 100644 --- a/nixos/modules/system/boot/stage-2-init.sh +++ b/nixos/modules/system/boot/stage-2-init.sh @@ -155,6 +155,21 @@ mkdir -m 0755 -p /var/setuid-wrappers mount -t tmpfs -o "mode=0755" tmpfs /var/setuid-wrappers +# Log the script output to /dev/kmsg or /run/log/stage-2-init.log. +# Only at this point are all the necessary prerequisites ready for these commands. +exec {logOutFd}>&1 {logErrFd}>&2 +if test -w /dev/kmsg; then + exec > >(tee -i /proc/self/fd/"$logOutFd" | while read -r line; do + if test -n "$line"; then + echo "<7>stage-2-init: $line" > /dev/kmsg + fi + done) 2>&1 +else + mkdir -p /run/log + exec > >(tee -i /run/log/stage-2-init.log) 2>&1 +fi + + # Run the script that performs all configuration activation that does # not have to be done at boot time. echo "running activation script..." @@ -182,6 +197,11 @@ ln -sfn /run/booted-system /nix/var/nix/gcroots/booted-system @shell@ @postBootCommands@ +# Reset the logging file descriptors. +exec 1>&$logOutFd 2>&$logErrFd +exec {logOutFd}>&- {logErrFd}>&- + + # Start systemd. echo "starting systemd..." PATH=/run/current-system/systemd/lib/systemd \ diff --git a/nixos/modules/system/boot/systemd.nix b/nixos/modules/system/boot/systemd.nix index 211e0423216e..a3c83521c354 100644 --- a/nixos/modules/system/boot/systemd.nix +++ b/nixos/modules/system/boot/systemd.nix @@ -61,6 +61,8 @@ let "systemd-user-sessions.service" "dbus-org.freedesktop.login1.service" "dbus-org.freedesktop.machine1.service" + "org.freedesktop.login1.busname" + "org.freedesktop.machine1.busname" "user@.service" # Journal. @@ -147,13 +149,18 @@ let "systemd-tmpfiles-setup-dev.service" # Misc. + "org.freedesktop.systemd1.busname" "systemd-sysctl.service" "dbus-org.freedesktop.timedate1.service" "dbus-org.freedesktop.locale1.service" "dbus-org.freedesktop.hostname1.service" + "org.freedesktop.timedate1.busname" + "org.freedesktop.locale1.busname" + "org.freedesktop.hostname1.busname" "systemd-timedated.service" "systemd-localed.service" "systemd-hostnamed.service" + "systemd-binfmt.service" ] ++ cfg.additionalUpstreamSystemUnits; @@ -773,6 +780,7 @@ in systemd.services.systemd-user-sessions.restartIfChanged = false; # Restart kills all active sessions. systemd.targets.local-fs.unitConfig.X-StopOnReconfiguration = true; systemd.targets.remote-fs.unitConfig.X-StopOnReconfiguration = true; + systemd.services.systemd-binfmt.wants = [ "proc-sys-fs-binfmt_misc.automount" ]; # Don't bother with certain units in containers. systemd.services.systemd-remount-fs.unitConfig.ConditionVirtualization = "!container"; diff --git a/nixos/modules/tasks/filesystems.nix b/nixos/modules/tasks/filesystems.nix index d0dd7670157e..4d1466db22d0 100644 --- a/nixos/modules/tasks/filesystems.nix +++ b/nixos/modules/tasks/filesystems.nix @@ -41,11 +41,15 @@ let }; options = mkOption { - default = "defaults"; - example = "data=journal"; - type = types.commas; # FIXME: should be a list + default = [ "defaults" ]; + example = [ "data=journal" ]; description = "Options used to mount the file system."; - }; + } // (if versionAtLeast lib.nixpkgsVersion "16.09" then { + type = types.listOf types.str; + } else { + type = types.either types.commas (types.listOf types.str); + apply = x: if isList x then x else lib.strings.splitString "," (builtins.trace "warning: passing a comma-separated string for filesystem options is deprecated; use a list of strings instead. This will become a hard error in 16.09." x); + }); autoFormat = mkOption { default = false; @@ -112,7 +116,7 @@ in "/data" = { device = "/dev/hda2"; fsType = "ext3"; - options = "data=journal"; + options = [ "data=journal" ]; }; "/bigdisk".label = "bigdisk"; }; @@ -127,7 +131,7 @@ in <command>mount</command>; defaults to <literal>"auto"</literal>), and <literal>options</literal> (the mount options passed to <command>mount</command> using the - <option>-o</option> flag; defaults to <literal>"defaults"</literal>). + <option>-o</option> flag; defaults to <literal>[ "defaults" ]</literal>). Instead of specifying <literal>device</literal>, you can also specify a volume label (<literal>label</literal>) for file @@ -177,7 +181,7 @@ in else throw "No device specified for mount point ‘${fs.mountPoint}’.") + " " + fs.mountPoint + " " + fs.fsType - + " " + fs.options + + " " + builtins.concatStringsSep "," fs.options + " 0" + " " + (if skipCheck fs then "0" else if fs.mountPoint == "/" then "1" else "2") diff --git a/nixos/modules/tasks/kbd.nix b/nixos/modules/tasks/kbd.nix index e1574fa68ad9..02721bb3bea2 100644 --- a/nixos/modules/tasks/kbd.nix +++ b/nixos/modules/tasks/kbd.nix @@ -12,6 +12,8 @@ let FONT=${config.i18n.consoleFont} ${colors} ''; + + setVconsole = !config.boot.isContainer; in { @@ -41,26 +43,33 @@ in ###### implementation - config = { - - environment.systemPackages = [ pkgs.kbd ]; - - # Let systemd-vconsole-setup.service do the work of setting up the - # virtual consoles. FIXME: trigger a restart of - # systemd-vconsole-setup.service if /etc/vconsole.conf changes. - environment.etc."vconsole.conf".source = vconsoleConf; - - # This is identical to the systemd-vconsole-setup.service unit - # shipped with systemd, except that it uses /dev/tty1 instead of - # /dev/tty0 to prevent putting the X server in non-raw mode, and - # it has a restart trigger. - systemd.services."systemd-vconsole-setup" = - { wantedBy = [ "multi-user.target" ]; - before = [ "display-manager.service" ]; - after = [ "systemd-udev-settle.service" ]; - restartTriggers = [ vconsoleConf ]; - }; - - }; + config = mkMerge [ + (mkIf (!setVconsole) { + systemd.services."systemd-vconsole-setup".enable = false; + }) + + (mkIf setVconsole { + environment.systemPackages = [ pkgs.kbd ]; + + # Let systemd-vconsole-setup.service do the work of setting up the + # virtual consoles. FIXME: trigger a restart of + # systemd-vconsole-setup.service if /etc/vconsole.conf changes. + environment.etc = [ { + target = "vconsole.conf"; + source = vconsoleConf; + } ]; + + # This is identical to the systemd-vconsole-setup.service unit + # shipped with systemd, except that it uses /dev/tty1 instead of + # /dev/tty0 to prevent putting the X server in non-raw mode, and + # it has a restart trigger. + systemd.services."systemd-vconsole-setup" = + { wantedBy = [ "multi-user.target" ]; + before = [ "display-manager.service" ]; + after = [ "systemd-udev-settle.service" ]; + restartTriggers = [ vconsoleConf ]; + }; + }) + ]; } diff --git a/nixos/modules/tasks/network-interfaces-scripted.nix b/nixos/modules/tasks/network-interfaces-scripted.nix index f07e7baeb119..c960e401f9b1 100644 --- a/nixos/modules/tasks/network-interfaces-scripted.nix +++ b/nixos/modules/tasks/network-interfaces-scripted.nix @@ -144,15 +144,12 @@ in fi ${config.systemd.package}/bin/systemctl start ip-up.target ''; - preStop = - '' - echo "releasing configured ip's..." - '' + flip concatMapStrings (ips) (ip: + preStop = flip concatMapStrings (ips) (ip: let address = "${ip.address}/${toString ip.prefixLength}"; in '' - echo -n "Deleting ${address}..." + echo -n "deleting ${address}..." ip addr del "${address}" dev "${i.name}" >/dev/null 2>&1 || echo -n " Failed" echo "" ''); diff --git a/nixos/modules/tasks/network-interfaces.nix b/nixos/modules/tasks/network-interfaces.nix index e72c0f8956ed..503d3813611f 100644 --- a/nixos/modules/tasks/network-interfaces.nix +++ b/nixos/modules/tasks/network-interfaces.nix @@ -927,7 +927,7 @@ in pkgs.nettools pkgs.openresolv ] - ++ optionals (!config.boot.isContainer) [ + ++ optionals config.networking.wireless.enable [ pkgs.wirelesstools # FIXME: obsolete? pkgs.iw pkgs.rfkill diff --git a/nixos/modules/testing/test-instrumentation.nix b/nixos/modules/testing/test-instrumentation.nix index c233beb63ca7..f0f56b17f20f 100644 --- a/nixos/modules/testing/test-instrumentation.nix +++ b/nixos/modules/testing/test-instrumentation.nix @@ -20,7 +20,15 @@ let kernel = config.boot.kernelPackages.kernel; in export USER=root export HOME=/root export DISPLAY=:0.0 + source /etc/profile + + # Don't use a pager when executing backdoor + # actions. Because we use a tty, commands like systemctl + # or nix-store get confused into thinking they're running + # interactively. + export PAGER= + cd /tmp exec < /dev/hvc0 > /dev/hvc0 while ! exec 2> /dev/ttyS0; do sleep 0.1; done @@ -38,11 +46,6 @@ let kernel = config.boot.kernelPackages.kernel; in systemd.services."serial-getty@ttyS0".enable = false; systemd.services."serial-getty@hvc0".enable = false; - # Don't use a pager when executing backdoor actions. Because we - # use a tty, commands like systemctl or nix-store get confused - # into thinking they're running interactively. - environment.variables.PAGER = ""; - boot.initrd.preDeviceCommands = '' echo 600 > /proc/sys/kernel/hung_task_timeout_secs diff --git a/nixos/modules/virtualisation/amazon-image.nix b/nixos/modules/virtualisation/amazon-image.nix index 7ccc9df740e5..a895f66db8ef 100644 --- a/nixos/modules/virtualisation/amazon-image.nix +++ b/nixos/modules/virtualisation/amazon-image.nix @@ -20,8 +20,7 @@ let cfg = config.ec2; in autoResize = true; }; - boot.initrd.kernelModules = [ "xen-blkfront" ]; - boot.kernelModules = [ "xen-netfront" ]; + boot.initrd.kernelModules = [ "xen-blkfront" "xen-netfront" ]; boot.kernelParams = mkIf cfg.hvm [ "console=ttyS0" ]; # Prevent the nouveau kernel module from being loaded, as it @@ -44,6 +43,8 @@ let cfg = config.ec2; in kill -9 -1 ''; + boot.initrd.network.enable = true; + # Mount all formatted ephemeral disks and activate all swap devices. # We cannot do this with the ‘fileSystems’ and ‘swapDevices’ options # because the set of devices is dependent on the instance type @@ -55,6 +56,28 @@ let cfg = config.ec2; in # Nix operations. boot.initrd.postMountCommands = '' + metaDir=$targetRoot/etc/ec2-metadata + mkdir -m 0755 -p "$metaDir" + + echo "getting EC2 instance metadata..." + + if ! [ -e "$metaDir/ami-manifest-path" ]; then + wget -q -O "$metaDir/ami-manifest-path" http://169.254.169.254/1.0/meta-data/ami-manifest-path + fi + + if ! [ -e "$metaDir/user-data" ]; then + wget -q -O "$metaDir/user-data" http://169.254.169.254/1.0/user-data + chmod 600 "$metaDir/user-data" + fi + + if ! [ -e "$metaDir/hostname" ]; then + wget -q -O "$metaDir/hostname" http://169.254.169.254/1.0/meta-data/hostname + fi + + if ! [ -e "$metaDir/public-keys-0-openssh-key" ]; then + wget -q -O "$metaDir/public-keys-0-openssh-key" http://169.254.169.254/1.0/meta-data/public-keys/0/openssh-key + fi + diskNr=0 diskForUnionfs= for device in /dev/xvd[abcde]*; do @@ -81,7 +104,7 @@ let cfg = config.ec2; in mkdir -m 1777 -p $targetRoot/$diskForUnionfs/root/tmp $targetRoot/tmp mount --bind $targetRoot/$diskForUnionfs/root/tmp $targetRoot/tmp - if [ ! -e $targetRoot/.ebs ]; then + if [ "$(cat "$metaDir/ami-manifest-path")" != "(unknown)" ]; then mkdir -m 755 -p $targetRoot/$diskForUnionfs/root/var $targetRoot/var mount --bind $targetRoot/$diskForUnionfs/root/var $targetRoot/var diff --git a/nixos/modules/virtualisation/amazon-init.nix b/nixos/modules/virtualisation/amazon-init.nix index 96cd57e6db5d..886552f33c2c 100644 --- a/nixos/modules/virtualisation/amazon-init.nix +++ b/nixos/modules/virtualisation/amazon-init.nix @@ -4,18 +4,17 @@ let bootScript = pkgs.writeScript "bootscript.sh" '' - #!${pkgs.stdenv.shell} -eux + #!${pkgs.stdenv.shell} -eu - echo "attempting to fetch configuration from user-data..." + echo "attempting to fetch configuration from EC2 user data..." - export PATH=${config.nix.package}/bin:${pkgs.wget}/bin:${pkgs.systemd}/bin:${pkgs.gnugrep}/bin:${pkgs.gnused}/bin:${config.system.build.nixos-rebuild}/bin:$PATH + export PATH=${config.nix.package}/bin:${pkgs.systemd}/bin:${pkgs.gnugrep}/bin:${pkgs.gnused}/bin:${config.system.build.nixos-rebuild}/bin:$PATH export NIX_PATH=/nix/var/nix/profiles/per-user/root/channels/nixos:nixos-config=/etc/nixos/configuration.nix:/nix/var/nix/profiles/per-user/root/channels - userData="$(mktemp)" - wget -q --wait=1 --tries=0 --retry-connrefused -O - http://169.254.169.254/2011-01-01/user-data > "$userData" + userData=/etc/ec2-metadata/user-data + + if [ -s "$userData" ]; then - if [[ $? -eq 0 ]]; then - echo "user-data fetched" # If the user-data looks like it could be a nix expression, # copy it over. Also, look for a magic three-hash comment and set # that as the channel. @@ -30,17 +29,17 @@ let nix-channel --update fi - echo "setting configuration" + echo "setting configuration from EC2 user data" cp "$userData" /etc/nixos/configuration.nix else - echo "user-data does not appear to be a nix expression; ignoring" + echo "user data does not appear to be a Nix expression; ignoring" + exit fi else - echo "failed to fetch user-data" + echo "no user data is available" + exit fi - type -f nixos-rebuild - nixos-rebuild switch ''; in { diff --git a/nixos/modules/virtualisation/azure-agent.nix b/nixos/modules/virtualisation/azure-agent.nix index ef4e3e1e48d4..640519758c74 100644 --- a/nixos/modules/virtualisation/azure-agent.nix +++ b/nixos/modules/virtualisation/azure-agent.nix @@ -8,10 +8,11 @@ let waagent = with pkgs; stdenv.mkDerivation rec { name = "waagent-2.0"; - src = pkgs.fetchgit { - url = https://github.com/Phreedom/WALinuxAgent.git; - rev = "9dba81c7b1239c7971ec96e405e403c7cd224e6b"; - sha256 = "0khxk3ns3z37v26f2qj6m3m698a0vqpc9bxg5p7fyr3xza5gzwhs"; + src = pkgs.fetchFromGitHub { + owner = "Azure"; + repo = "WALinuxAgent"; + rev = "1b3a8407a95344d9d12a2a377f64140975f1e8e4"; + sha256 = "10byzvmpgrmr4d5mdn2kq04aapqb3sgr1admk13wjmy5cd6bwd2x"; }; buildInputs = [ makeWrapper python pythonPackages.wrapPython ]; runtimeDeps = [ findutils gnugrep gawk coreutils openssl openssh @@ -156,12 +157,6 @@ in after = [ "ip-up.target" ]; wants = [ "ip-up.target" ]; - environment = { - GIT_SSL_CAINFO = "/etc/ssl/certs/ca-certificates.crt"; - OPENSSL_X509_CERT_FILE = "/etc/ssl/certs/ca-certificates.crt"; - SSL_CERT_FILE = "/etc/ssl/certs/ca-certificates.crt"; - }; - path = [ pkgs.e2fsprogs ]; description = "Windows Azure Agent Service"; unitConfig.ConditionPathExists = "/etc/waagent.conf"; diff --git a/nixos/modules/virtualisation/azure-config-user.nix b/nixos/modules/virtualisation/azure-config-user.nix new file mode 100644 index 000000000000..de1b3857923f --- /dev/null +++ b/nixos/modules/virtualisation/azure-config-user.nix @@ -0,0 +1,12 @@ +{ config, pkgs, modulesPath, ... }: + +{ + # To build the configuration or use nix-env, you need to run + # either nixos-rebuild --upgrade or nix-channel --update + # to fetch the nixos channel. + + # This configures everything but bootstrap services, + # which only need to be run once and have already finished + # if you are able to see this comment. + imports = [ "${modulesPath}/virtualisation/azure-common.nix" ]; +} diff --git a/nixos/modules/virtualisation/azure-image.nix b/nixos/modules/virtualisation/azure-image.nix index 08944e641d76..79d1f7d7cc43 100644 --- a/nixos/modules/virtualisation/azure-image.nix +++ b/nixos/modules/virtualisation/azure-image.nix @@ -16,14 +16,14 @@ in cyl=$(((${diskSize}*1024*1024)/(512*63*255))) size=$(($cyl*255*63*512)) roundedsize=$((($size/(1024*1024)+1)*(1024*1024))) - ${pkgs.vmTools.qemu}/bin/qemu-img create -f raw $diskImage $roundedsize + ${pkgs.vmTools.qemu-220}/bin/qemu-img create -f raw $diskImage $roundedsize mv closure xchg/ ''; postVM = '' mkdir -p $out - ${pkgs.vmTools.qemu}/bin/qemu-img convert -f raw -O vpc $diskImage $out/disk.vhd + ${pkgs.vmTools.qemu-220}/bin/qemu-img convert -f raw -O vpc -o subformat=fixed $diskImage $out/disk.vhd rm $diskImage ''; diskImageBase = "nixos-image-${config.system.nixosLabel}-${pkgs.stdenv.system}.raw"; @@ -78,7 +78,7 @@ in echo Install a configuration.nix. mkdir -p /mnt/etc/nixos /mnt/boot/grub - cp ${./azure-config.nix} /mnt/etc/nixos/configuration.nix + cp ${./azure-config-user.nix} /mnt/etc/nixos/configuration.nix echo Generate the GRUB menu. ln -s vda /dev/sda diff --git a/nixos/modules/virtualisation/azure-qemu-220-no-etc-install.patch b/nixos/modules/virtualisation/azure-qemu-220-no-etc-install.patch new file mode 100644 index 000000000000..81d29feea3de --- /dev/null +++ b/nixos/modules/virtualisation/azure-qemu-220-no-etc-install.patch @@ -0,0 +1,14 @@ +diff --git a/Makefile b/Makefile +index d6b9dc1..ce7c493 100644 +--- a/Makefile ++++ b/Makefile +@@ -384,8 +384,7 @@ install-confdir: + install-sysconfig: install-datadir install-confdir + $(INSTALL_DATA) $(SRC_PATH)/sysconfigs/target/target-x86_64.conf "$(DESTDIR)$(qemu_confdir)" + +-install: all $(if $(BUILD_DOCS),install-doc) install-sysconfig \ +-install-datadir install-localstatedir ++install: all $(if $(BUILD_DOCS),install-doc) install-datadir + ifneq ($(TOOLS),) + $(call install-prog,$(TOOLS),$(DESTDIR)$(bindir)) + endif diff --git a/nixos/modules/virtualisation/container-config.nix b/nixos/modules/virtualisation/container-config.nix index c210c8d5f25e..b4f9d8b6fc17 100644 --- a/nixos/modules/virtualisation/container-config.nix +++ b/nixos/modules/virtualisation/container-config.nix @@ -19,6 +19,9 @@ with lib; # Shut up warnings about not having a boot loader. system.build.installBootLoader = "${pkgs.coreutils}/bin/true"; + # Not supported in systemd-nspawn containers. + security.audit.enable = false; + }; } diff --git a/nixos/modules/virtualisation/ec2-data.nix b/nixos/modules/virtualisation/ec2-data.nix index 383750520ab7..db3dd9949c12 100644 --- a/nixos/modules/virtualisation/ec2-data.nix +++ b/nixos/modules/virtualisation/ec2-data.nix @@ -1,6 +1,6 @@ -# This module defines a systemd service that obtains the SSH key and -# host name of virtual machines running on Amazon EC2, Eucalyptus and -# OpenStack Compute (Nova). +# This module defines a systemd service that sets the SSH host key and +# authorized client key and host name of virtual machines running on +# Amazon EC2, Eucalyptus and OpenStack Compute (Nova). { config, lib, pkgs, ... }: @@ -9,58 +9,54 @@ with lib; { config = { - systemd.services.fetch-ec2-data = - { description = "Fetch EC2 Data"; + systemd.services.apply-ec2-data = + { description = "Apply EC2 Data"; wantedBy = [ "multi-user.target" "sshd.service" ]; before = [ "sshd.service" ]; - wants = [ "ip-up.target" ]; - after = [ "ip-up.target" ]; - path = [ pkgs.wget pkgs.iproute ]; + path = [ pkgs.iproute ]; script = '' - wget="wget -q --retry-connrefused -O -" - ${optionalString (config.networking.hostName == "") '' echo "setting host name..." - ${pkgs.nettools}/bin/hostname $($wget http://169.254.169.254/1.0/meta-data/hostname) + if [ -s /etc/ec2-metadata/hostname ]; then + ${pkgs.nettools}/bin/hostname $(cat /etc/ec2-metadata/hostname) + fi ''} - # Don't download the SSH key if it has already been injected - # into the image (a Nova feature). if ! [ -e /root/.ssh/authorized_keys ]; then echo "obtaining SSH key..." mkdir -m 0700 -p /root/.ssh - $wget http://169.254.169.254/1.0/meta-data/public-keys/0/openssh-key > /root/key.pub - if [ $? -eq 0 -a -e /root/key.pub ]; then - cat /root/key.pub >> /root/.ssh/authorized_keys + if [ -s /etc/ec2-metadata/public-keys-0-openssh-key ]; then + cat /etc/ec2-metadata/public-keys-0-openssh-key >> /root/.ssh/authorized_keys echo "new key added to authorized_keys" chmod 600 /root/.ssh/authorized_keys - rm -f /root/key.pub fi fi # Extract the intended SSH host key for this machine from # the supplied user data, if available. Otherwise sshd will # generate one normally. - $wget http://169.254.169.254/2011-01-01/user-data > /root/user-data || true + userData=/etc/ec2-metadata/user-data mkdir -m 0755 -p /etc/ssh - key="$(sed 's/|/\n/g; s/SSH_HOST_DSA_KEY://; t; d' /root/user-data)" - key_pub="$(sed 's/SSH_HOST_DSA_KEY_PUB://; t; d' /root/user-data)" - if [ -n "$key" -a -n "$key_pub" -a ! -e /etc/ssh/ssh_host_dsa_key ]; then - (umask 077; echo "$key" > /etc/ssh/ssh_host_dsa_key) - echo "$key_pub" > /etc/ssh/ssh_host_dsa_key.pub - fi - - key="$(sed 's/|/\n/g; s/SSH_HOST_ED25519_KEY://; t; d' /root/user-data)" - key_pub="$(sed 's/SSH_HOST_ED25519_KEY_PUB://; t; d' /root/user-data)" - if [ -n "$key" -a -n "$key_pub" -a ! -e /etc/ssh/ssh_host_ed25519_key ]; then - (umask 077; echo "$key" > /etc/ssh/ssh_host_ed25519_key) - echo "$key_pub" > /etc/ssh/ssh_host_ed25519_key.pub + if [ -s "$userData" ]; then + key="$(sed 's/|/\n/g; s/SSH_HOST_DSA_KEY://; t; d' $userData)" + key_pub="$(sed 's/SSH_HOST_DSA_KEY_PUB://; t; d' $userData)" + if [ -n "$key" -a -n "$key_pub" -a ! -e /etc/ssh/ssh_host_dsa_key ]; then + (umask 077; echo "$key" > /etc/ssh/ssh_host_dsa_key) + echo "$key_pub" > /etc/ssh/ssh_host_dsa_key.pub + fi + + key="$(sed 's/|/\n/g; s/SSH_HOST_ED25519_KEY://; t; d' $userData)" + key_pub="$(sed 's/SSH_HOST_ED25519_KEY_PUB://; t; d' $userData)" + if [ -n "$key" -a -n "$key_pub" -a ! -e /etc/ssh/ssh_host_ed25519_key ]; then + (umask 077; echo "$key" > /etc/ssh/ssh_host_ed25519_key) + echo "$key_pub" > /etc/ssh/ssh_host_ed25519_key.pub + fi fi ''; diff --git a/nixos/modules/virtualisation/nixos-container.pl b/nixos/modules/virtualisation/nixos-container.pl index 004385f728c6..eda57a9751eb 100644..100755 --- a/nixos/modules/virtualisation/nixos-container.pl +++ b/nixos/modules/virtualisation/nixos-container.pl @@ -97,10 +97,10 @@ if ($action eq "create") { if ($ensureUniqueName) { my $base = $containerName; for (my $nr = 0; ; $nr++) { - $containerName = "$base-$nr"; $confFile = "/etc/containers/$containerName.conf"; $root = "/var/lib/containers/$containerName"; last unless -e $confFile || -e $root; + $containerName = "$base-$nr"; } } diff --git a/nixos/modules/virtualisation/nova.nix b/nixos/modules/virtualisation/nova.nix index f356445abe46..c2837d0e2e24 100644 --- a/nixos/modules/virtualisation/nova.nix +++ b/nixos/modules/virtualisation/nova.nix @@ -146,7 +146,7 @@ in path = [ pkgs.sudo pkgs.vlan pkgs.nettools pkgs.iptables pkgs.qemu_kvm - pkgs.e2fsprogs pkgs.utillinux pkgs.multipath_tools pkgs.iproute + pkgs.e2fsprogs pkgs.utillinux pkgs.multipath-tools pkgs.iproute pkgs.bridge-utils ]; diff --git a/nixos/modules/virtualisation/qemu-vm.nix b/nixos/modules/virtualisation/qemu-vm.nix index 82b58aa67a3d..7dfbc38efee6 100644 --- a/nixos/modules/virtualisation/qemu-vm.nix +++ b/nixos/modules/virtualisation/qemu-vm.nix @@ -110,6 +110,7 @@ let # Generate a hard disk image containing a /boot partition and GRUB # in the MBR. Used when the `useBootLoader' option is set. + # FIXME: use nixos/lib/make-disk-image.nix. bootDisk = pkgs.vmTools.runInLinuxVM ( pkgs.runCommand "nixos-boot-disk" @@ -427,38 +428,38 @@ in ${if cfg.writableStore then "/nix/.ro-store" else "/nix/store"} = { device = "store"; fsType = "9p"; - options = "trans=virtio,version=9p2000.L,cache=loose"; + options = [ "trans=virtio" "version=9p2000.L" "cache=loose" ]; neededForBoot = true; }; "/tmp/xchg" = { device = "xchg"; fsType = "9p"; - options = "trans=virtio,version=9p2000.L,cache=loose"; + options = [ "trans=virtio" "version=9p2000.L" "cache=loose" ]; neededForBoot = true; }; "/tmp/shared" = { device = "shared"; fsType = "9p"; - options = "trans=virtio,version=9p2000.L"; + options = [ "trans=virtio" "version=9p2000.L" ]; neededForBoot = true; }; } // optionalAttrs cfg.writableStore { "/nix/store" = { fsType = "unionfs-fuse"; device = "unionfs"; - options = "allow_other,cow,nonempty,chroot=/mnt-root,max_files=32768,hide_meta_files,dirs=/nix/.rw-store=rw:/nix/.ro-store=ro"; + options = [ "allow_other" "cow" "nonempty" "chroot=/mnt-root" "max_files=32768" "hide_meta_files" "dirs=/nix/.rw-store=rw:/nix/.ro-store=ro" ]; }; } // optionalAttrs (cfg.writableStore && cfg.writableStoreUseTmpfs) { "/nix/.rw-store" = { fsType = "tmpfs"; - options = "mode=0755"; + options = [ "mode=0755" ]; neededForBoot = true; }; } // optionalAttrs cfg.useBootLoader { "/boot" = { device = "/dev/vdb2"; fsType = "vfat"; - options = "ro"; + options = [ "ro" ]; noCheck = true; # fsck fails on a r/o filesystem }; }); diff --git a/nixos/modules/virtualisation/rkt.nix b/nixos/modules/virtualisation/rkt.nix index 7b4d46e0749e..c4c5cb3380e8 100644 --- a/nixos/modules/virtualisation/rkt.nix +++ b/nixos/modules/virtualisation/rkt.nix @@ -58,5 +58,7 @@ in ExecStart = "${pkgs.rkt}/bin/rkt gc ${cfg.gc.options}"; }; }; + + users.extraGroups.rkt = {}; }; } diff --git a/nixos/release-combined.nix b/nixos/release-combined.nix index 06890b458efa..958e587444d8 100644 --- a/nixos/release-combined.nix +++ b/nixos/release-combined.nix @@ -48,7 +48,7 @@ in rec { (all nixos.ova) #(all nixos.tests.containers) - (all nixos.tests.chromium) + (all nixos.tests.chromium.stable) (all nixos.tests.firefox) (all nixos.tests.firewall) nixos.tests.gnome3.x86_64-linux # FIXME: i686-linux @@ -63,7 +63,7 @@ in rec { (all nixos.tests.installer.btrfsSimple) (all nixos.tests.installer.btrfsSubvols) (all nixos.tests.installer.btrfsSubvolDefault) - (all nixos.tests.bootBiosCdrom) + (all nixos.tests.boot.biosCdrom) (all nixos.tests.ipv6) (all nixos.tests.kde4) #(all nixos.tests.lightdm) diff --git a/nixos/release.nix b/nixos/release.nix index 8a502ae2baa1..069cf3727de7 100644 --- a/nixos/release.nix +++ b/nixos/release.nix @@ -13,7 +13,25 @@ let forAllSystems = genAttrs supportedSystems; - callTest = fn: args: forAllSystems (system: hydraJob (import fn ({ inherit system; } // args))); + importTest = fn: args: system: import fn ({ + inherit system; + } // args); + + callTest = fn: args: forAllSystems (system: hydraJob (importTest fn args system)); + + callSubTests = fn: args: let + discover = attrs: let + subTests = filterAttrs (const (hasAttr "test")) attrs; + in mapAttrs (const (t: hydraJob t.test)) subTests; + + discoverForSystem = system: mapAttrs (_: test: { + ${system} = test; + }) (discover (importTest fn args system)); + + # If the test is only for a particular system, use only the specified + # system instead of generating attributes for all available systems. + in if args ? system then discover (import fn args) + else foldAttrs mergeAttrs {} (map discoverForSystem supportedSystems); pkgs = import nixpkgs { system = "x86_64-linux"; }; @@ -113,7 +131,7 @@ in rec { }); iso_graphical = forAllSystems (system: makeIso { - module = ./modules/installer/cd-dvd/installation-cd-graphical.nix; + module = ./modules/installer/cd-dvd/installation-cd-graphical-kde.nix; type = "graphical"; inherit system; }); @@ -215,8 +233,9 @@ in rec { tests.avahi = callTest tests/avahi.nix {}; tests.bittorrent = callTest tests/bittorrent.nix {}; tests.blivet = callTest tests/blivet.nix {}; + tests.boot = callSubTests tests/boot.nix {}; tests.cadvisor = hydraJob (import tests/cadvisor.nix { system = "x86_64-linux"; }); - tests.chromium = callTest tests/chromium.nix {}; + tests.chromium = callSubTests tests/chromium.nix {}; tests.cjdns = callTest tests/cjdns.nix {}; tests.containers = callTest tests/containers.nix {}; tests.docker = hydraJob (import tests/docker.nix { system = "x86_64-linux"; }); @@ -230,23 +249,14 @@ in rec { #tests.gitlab = callTest tests/gitlab.nix {}; tests.gnome3 = callTest tests/gnome3.nix {}; tests.gnome3-gdm = callTest tests/gnome3-gdm.nix {}; + tests.grsecurity = callTest tests/grsecurity.nix {}; tests.i3wm = callTest tests/i3wm.nix {}; - tests.installer.grub1 = forAllSystems (system: hydraJob (import tests/installer.nix { inherit system; }).grub1.test); - tests.installer.lvm = forAllSystems (system: hydraJob (import tests/installer.nix { inherit system; }).lvm.test); - tests.installer.luksroot = forAllSystems (system: hydraJob (import tests/installer.nix { inherit system; }).luksroot.test); - tests.installer.separateBoot = forAllSystems (system: hydraJob (import tests/installer.nix { inherit system; }).separateBoot.test); - tests.installer.separateBootFat = forAllSystems (system: hydraJob (import tests/installer.nix { inherit system; }).separateBootFat.test); - tests.installer.simple = forAllSystems (system: hydraJob (import tests/installer.nix { inherit system; }).simple.test); - tests.installer.simpleLabels = forAllSystems (system: hydraJob (import tests/installer.nix { inherit system; }).simpleLabels.test); - tests.installer.simpleProvided = forAllSystems (system: hydraJob (import tests/installer.nix { inherit system; }).simpleProvided.test); - tests.installer.swraid = forAllSystems (system: hydraJob (import tests/installer.nix { inherit system; }).swraid.test); - tests.installer.btrfsSimple = forAllSystems (system: hydraJob (import tests/installer.nix { inherit system; }).btrfsSimple.test); - tests.installer.btrfsSubvols = forAllSystems (system: hydraJob (import tests/installer.nix { inherit system; }).btrfsSubvols.test); - tests.installer.btrfsSubvolDefault = forAllSystems (system: hydraJob (import tests/installer.nix { inherit system; }).btrfsSubvolDefault.test); + tests.installer = callSubTests tests/installer.nix {}; tests.influxdb = callTest tests/influxdb.nix {}; tests.ipv6 = callTest tests/ipv6.nix {}; tests.jenkins = callTest tests/jenkins.nix {}; tests.kde4 = callTest tests/kde4.nix {}; + tests.initrdNetwork = callTest tests/initrd-network.nix {}; tests.kubernetes = hydraJob (import tests/kubernetes.nix { system = "x86_64-linux"; }); tests.latestKernel.login = callTest tests/login.nix { latestKernel = true; }; #tests.lightdm = callTest tests/lightdm.nix {}; @@ -260,33 +270,17 @@ in rec { tests.mysqlReplication = callTest tests/mysql-replication.nix {}; tests.nat.firewall = callTest tests/nat.nix { withFirewall = true; }; tests.nat.standalone = callTest tests/nat.nix { withFirewall = false; }; - tests.networking.networkd.loopback = callTest tests/networking.nix { networkd = true; test = "loopback"; }; - tests.networking.networkd.static = callTest tests/networking.nix { networkd = true; test = "static"; }; - tests.networking.networkd.dhcpSimple = callTest tests/networking.nix { networkd = true; test = "dhcpSimple"; }; - tests.networking.networkd.dhcpOneIf = callTest tests/networking.nix { networkd = true; test = "dhcpOneIf"; }; - tests.networking.networkd.bond = callTest tests/networking.nix { networkd = true; test = "bond"; }; - tests.networking.networkd.bridge = callTest tests/networking.nix { networkd = true; test = "bridge"; }; - tests.networking.networkd.macvlan = callTest tests/networking.nix { networkd = true; test = "macvlan"; }; - tests.networking.networkd.sit = callTest tests/networking.nix { networkd = true; test = "sit"; }; - tests.networking.networkd.vlan = callTest tests/networking.nix { networkd = true; test = "vlan"; }; - tests.networking.scripted.loopback = callTest tests/networking.nix { networkd = false; test = "loopback"; }; - tests.networking.scripted.static = callTest tests/networking.nix { networkd = false; test = "static"; }; - tests.networking.scripted.dhcpSimple = callTest tests/networking.nix { networkd = false; test = "dhcpSimple"; }; - tests.networking.scripted.dhcpOneIf = callTest tests/networking.nix { networkd = false; test = "dhcpOneIf"; }; - tests.networking.scripted.bond = callTest tests/networking.nix { networkd = false; test = "bond"; }; - tests.networking.scripted.bridge = callTest tests/networking.nix { networkd = false; test = "bridge"; }; - tests.networking.scripted.macvlan = callTest tests/networking.nix { networkd = false; test = "macvlan"; }; - tests.networking.scripted.sit = callTest tests/networking.nix { networkd = false; test = "sit"; }; - tests.networking.scripted.vlan = callTest tests/networking.nix { networkd = false; test = "vlan"; }; + tests.networking.networkd = callSubTests tests/networking.nix { networkd = true; }; + tests.networking.scripted = callSubTests tests/networking.nix { networkd = false; }; # TODO: put in networking.nix after the test becomes more complete tests.networkingProxy = callTest tests/networking-proxy.nix {}; tests.nfs3 = callTest tests/nfs.nix { version = 3; }; tests.nfs4 = callTest tests/nfs.nix { version = 4; }; - tests.nixosPinVersion = callTest tests/nixos-pin-version.nix {}; tests.nsd = callTest tests/nsd.nix {}; tests.openssh = callTest tests/openssh.nix {}; tests.panamax = hydraJob (import tests/panamax.nix { system = "x86_64-linux"; }); tests.peerflix = callTest tests/peerflix.nix {}; + tests.postgresql = callTest tests/postgresql.nix {}; tests.printing = callTest tests/printing.nix {}; tests.proxy = callTest tests/proxy.nix {}; tests.pumpio = callTest tests/pump.io.nix {}; @@ -297,12 +291,8 @@ in rec { tests.simple = callTest tests/simple.nix {}; tests.tomcat = callTest tests/tomcat.nix {}; tests.udisks2 = callTest tests/udisks2.nix {}; - tests.virtualbox = hydraJob (import tests/virtualbox.nix { system = "x86_64-linux"; }); + tests.virtualbox = callSubTests tests/virtualbox.nix { system = "x86_64-linux"; }; tests.xfce = callTest tests/xfce.nix {}; - tests.bootBiosCdrom = forAllSystems (system: hydraJob (import tests/boot.nix { inherit system; }).bootBiosCdrom); - tests.bootBiosUsb = forAllSystems (system: hydraJob (import tests/boot.nix { inherit system; }).bootBiosUsb); - tests.bootUefiCdrom = forAllSystems (system: hydraJob (import tests/boot.nix { inherit system; }).bootUefiCdrom); - tests.bootUefiUsb = forAllSystems (system: hydraJob (import tests/boot.nix { inherit system; }).bootUefiUsb); /* Build a bunch of typical closures so that Hydra can keep track of diff --git a/nixos/tests/avahi.nix b/nixos/tests/avahi.nix index a8369a6d1f88..976a770e887c 100644 --- a/nixos/tests/avahi.nix +++ b/nixos/tests/avahi.nix @@ -5,18 +5,21 @@ import ./make-test.nix ({ pkgs, ... } : { maintainers = [ eelco chaoflow ]; }; - nodes = { - one = - { config, pkgs, ... }: { - services.avahi.enable = true; - services.avahi.nssmdns = true; - }; - - two = - { config, pkgs, ... }: { - services.avahi.enable = true; - services.avahi.nssmdns = true; + nodes = let + cfg = { config, pkgs, ... }: { + services.avahi = { + enable = true; + nssmdns = true; + publish.addresses = true; + publish.domain = true; + publish.enable = true; + publish.userServices = true; + publish.workstation = true; }; + }; + in { + one = cfg; + two = cfg; }; testScript = diff --git a/nixos/tests/boot.nix b/nixos/tests/boot.nix index 6a1d330155e1..905d16458825 100644 --- a/nixos/tests/boot.nix +++ b/nixos/tests/boot.nix @@ -30,17 +30,17 @@ let ''; }; in { - bootBiosCdrom = makeBootTest "bios-cdrom" '' + biosCdrom = makeBootTest "bios-cdrom" '' cdrom => glob("${iso}/iso/*.iso") ''; - bootBiosUsb = makeBootTest "bios-usb" '' + biosUsb = makeBootTest "bios-usb" '' usb => glob("${iso}/iso/*.iso") ''; - bootUefiCdrom = makeBootTest "uefi-cdrom" '' + uefiCdrom = makeBootTest "uefi-cdrom" '' cdrom => glob("${iso}/iso/*.iso"), bios => '${pkgs.OVMF}/FV/OVMF.fd' ''; - bootUefiUsb = makeBootTest "uefi-usb" '' + uefiUsb = makeBootTest "uefi-usb" '' usb => glob("${iso}/iso/*.iso"), bios => '${pkgs.OVMF}/FV/OVMF.fd' ''; diff --git a/nixos/tests/chromium.nix b/nixos/tests/chromium.nix index 1d1e12d0ee39..974af6888b69 100644 --- a/nixos/tests/chromium.nix +++ b/nixos/tests/chromium.nix @@ -1,13 +1,10 @@ -import ./make-test.nix ( -{ pkgs -, channelMap ? { - stable = pkgs.chromium; - beta = pkgs.chromiumBeta; - dev = pkgs.chromiumDev; - } -, ... -}: rec { - name = "chromium"; +{ system ? builtins.currentSystem }: + +with import ../lib/testing.nix { inherit system; }; +with pkgs.lib; + +mapAttrs (channel: chromiumPkg: makeTest rec { + name = "chromium-${channel}"; meta = with pkgs.stdenv.lib.maintainers; { maintainers = [ aszlig ]; }; @@ -16,6 +13,7 @@ import ./make-test.nix ( machine.imports = [ ./common/x11.nix ]; machine.virtualisation.memorySize = 2047; + machine.environment.systemPackages = [ chromiumPkg ]; startupHTML = pkgs.writeText "chromium-startup.html" '' <!DOCTYPE html> @@ -105,74 +103,65 @@ import ./make-test.nix ( closeWin; } - sub chromiumTest { - my ($channel, $pkg, $code) = @_; - $machine->waitForX; - - my $url = "file://${startupHTML}"; - my $args = "--user-data-dir=/tmp/chromium-$channel"; - $machine->execute( - "ulimit -c unlimited; ". - "$pkg/bin/chromium $args \"$url\" & disown" - ); - $machine->waitForText(qr/Type to search or enter a URL to navigate/); - $machine->waitUntilSucceeds("${xdo "check-startup" '' - search --sync --onlyvisible --name "startup done" - # close first start help popup - key -delay 1000 Escape + $machine->waitForX; + + my $url = "file://${startupHTML}"; + my $args = "--user-data-dir=/tmp/chromium-${channel}"; + $machine->execute( + "ulimit -c unlimited; ". + "chromium $args \"$url\" & disown" + ); + $machine->waitForText(qr/Type to search or enter a URL to navigate/); + $machine->waitUntilSucceeds("${xdo "check-startup" '' + search --sync --onlyvisible --name "startup done" + # close first start help popup + key -delay 1000 Escape + windowfocus --sync + windowactivate --sync + ''}"); + + createAndWaitForNewWin; + $machine->screenshot("empty_windows"); + closeWin; + + $machine->screenshot("startup_done"); + + testNewWin "check sandbox", sub { + $machine->succeed("${xdo "type-url" '' + search --sync --onlyvisible --name "new tab" windowfocus --sync - windowactivate --sync + type --delay 1000 "chrome://sandbox" ''}"); - createAndWaitForNewWin; - $machine->screenshot($channel."_emptywin"); - closeWin; - - $machine->screenshot($channel."_startup_done"); - - subtest("Chromium $channel", $code); - - $machine->shutdown; - } - - for (${let - mkArray = name: pkg: "[\"${name}\", \"${pkg}\"]"; - chanArrays = pkgs.lib.mapAttrsToList mkArray channelMap; - in pkgs.lib.concatStringsSep ", " chanArrays}) { - my ($channel, $pkg) = @$_; - chromiumTest $channel, $pkg, sub { - testNewWin "check sandbox", sub { - $machine->succeed("${xdo "type-url" '' - search --sync --onlyvisible --name "new tab" - windowfocus --sync - type --delay 1000 "chrome://sandbox" - ''}"); + $machine->succeed("${xdo "submit-url" '' + search --sync --onlyvisible --name "new tab" + windowfocus --sync + key --delay 1000 Return + ''}"); - $machine->succeed("${xdo "submit-url" '' - search --sync --onlyvisible --name "new tab" - windowfocus --sync - key --delay 1000 Return - ''}"); + $machine->screenshot("sandbox_info"); - $machine->screenshot($channel."_sandbox"); + $machine->succeed("${xdo "submit-url" '' + search --sync --onlyvisible --name "sandbox status" + windowfocus --sync + ''}"); + $machine->succeed("${xdo "submit-url" '' + key --delay 1000 Ctrl+a Ctrl+c + ''}"); - $machine->succeed("${xdo "submit-url" '' - search --sync --onlyvisible --name "sandbox status" - windowfocus --sync - ''}"); - $machine->succeed("${xdo "submit-url" '' - key --delay 1000 Ctrl+a Ctrl+c - ''}"); + my $clipboard = $machine->succeed("${pkgs.xclip}/bin/xclip -o"); + die "sandbox not working properly: $clipboard" + unless $clipboard =~ /namespace sandbox.*yes/mi + && $clipboard =~ /pid namespaces.*yes/mi + && $clipboard =~ /network namespaces.*yes/mi + && $clipboard =~ /seccomp.*sandbox.*yes/mi + && $clipboard =~ /you are adequately sandboxed/mi; + }; - my $clipboard = $machine->succeed("${pkgs.xclip}/bin/xclip -o"); - die "sandbox not working properly: $clipboard" - unless $clipboard =~ /namespace sandbox.*yes/mi - && $clipboard =~ /pid namespaces.*yes/mi - && $clipboard =~ /network namespaces.*yes/mi - && $clipboard =~ /seccomp.*sandbox.*yes/mi - && $clipboard =~ /you are adequately sandboxed/mi; - }; - }; - } + $machine->shutdown; ''; -}) +}) { + stable = pkgs.chromium; + beta = pkgs.chromiumBeta; + dev = pkgs.chromiumDev; +} diff --git a/nixos/tests/ec2.nix b/nixos/tests/ec2.nix index 1925ab37419a..0a95d6cfeff4 100644 --- a/nixos/tests/ec2.nix +++ b/nixos/tests/ec2.nix @@ -10,9 +10,10 @@ let inherit system; modules = [ ../maintainers/scripts/ec2/amazon-image.nix - ../../nixos/modules/testing/test-instrumentation.nix - { boot.initrd.kernelModules = [ "virtio" "virtio_blk" "virtio_pci" "virtio_ring" ]; - ec2.hvm = true; + ../modules/testing/test-instrumentation.nix + ../modules/profiles/minimal.nix + ../modules/profiles/qemu-guest.nix + { ec2.hvm = true; # Hack to make the partition resizing work in QEMU. boot.initrd.postDeviceCommands = mkBefore @@ -33,6 +34,7 @@ let ln -s ${pkgs.writeText "userData" userData} $out/2011-01-01/user-data mkdir -p $out/1.0/meta-data echo "${hostname}" > $out/1.0/meta-data/hostname + echo "(unknown)" > $out/1.0/meta-data/ami-manifest-path '' + optionalString (sshPublicKey != null) '' mkdir -p $out/1.0/meta-data/public-keys/0 ln -s ${pkgs.writeText "sshPublicKey" sshPublicKey} $out/1.0/meta-data/public-keys/0/openssh-key @@ -56,7 +58,7 @@ let # again when it deletes link-local addresses.) Ideally we'd # turn off the DHCP server, but qemu does not have an option # to do that. - my $startCommand = "qemu-kvm -m 768 -net nic -net 'user,net=169.0.0.0/8,guestfwd=tcp:169.254.169.254:80-cmd:${pkgs.micro-httpd}/bin/micro_httpd ${metaData}'"; + my $startCommand = "qemu-kvm -m 768 -net nic,vlan=0,model=virtio -net 'user,vlan=0,net=169.0.0.0/8,guestfwd=tcp:169.254.169.254:80-cmd:${pkgs.micro-httpd}/bin/micro_httpd ${metaData}'"; $startCommand .= " -drive file=$diskImage,if=virtio,werror=report"; $startCommand .= " \$QEMU_OPTS"; @@ -92,6 +94,8 @@ in { $machine->waitForFile("/root/user-data"); $machine->waitForUnit("sshd.service"); + $machine->succeed("grep unknown /etc/ec2-metadata/ami-manifest-path"); + # We have no keys configured on the client side yet, so this should fail $machine->fail("ssh -o BatchMode=yes localhost exit"); diff --git a/nixos/tests/grsecurity.nix b/nixos/tests/grsecurity.nix new file mode 100644 index 000000000000..14f1aa9ff885 --- /dev/null +++ b/nixos/tests/grsecurity.nix @@ -0,0 +1,19 @@ +# Basic test to make sure grsecurity works + +import ./make-test.nix ({ pkgs, ...} : { + name = "grsecurity"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ copumpkin ]; + }; + + machine = { config, pkgs, ... }: + { boot.kernelPackages = pkgs.linuxPackages_grsec_testing_server; }; + + testScript = + '' + $machine->succeed("uname -a") =~ /grsec/; + # FIXME: this seems to hang the whole test. Unclear why, but let's fix it + # $machine->succeed("${pkgs.paxtest}/bin/paxtest blackhat"); + ''; +}) + diff --git a/nixos/tests/haka.nix b/nixos/tests/haka.nix new file mode 100644 index 000000000000..40548f34690f --- /dev/null +++ b/nixos/tests/haka.nix @@ -0,0 +1,24 @@ +# This test runs haka and probes it with hakactl + +import ./make-test.nix ({ pkgs, ...} : { + name = "haka"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ tvestelind ]; + }; + + nodes = { + haka = + { config, pkgs, ... }: + { + services.haka.enable = true; + }; + }; + + testScript = '' + startAll; + + $haka->waitForUnit("haka.service"); + $haka->succeed("hakactl status"); + $haka->succeed("hakactl stop"); + ''; +}) diff --git a/nixos/tests/initrd-network.nix b/nixos/tests/initrd-network.nix new file mode 100644 index 000000000000..db9f572d3c2f --- /dev/null +++ b/nixos/tests/initrd-network.nix @@ -0,0 +1,22 @@ +import ./make-test.nix ({ pkgs, ...} : { + name = "initrd-network"; + + meta.maintainers = [ pkgs.stdenv.lib.maintainers.eelco ]; + + machine = { config, pkgs, ... }: { + imports = [ ../modules/profiles/minimal.nix ]; + boot.initrd.network.enable = true; + boot.initrd.network.postCommands = + '' + ip addr | grep 10.0.2.15 || exit 1 + ping -c1 10.0.2.2 || exit 1 + ''; + }; + + testScript = + '' + startAll; + $machine->waitForUnit("multi-user.target"); + $machine->succeed("ip link >&2"); + ''; +}) diff --git a/nixos/tests/installer.nix b/nixos/tests/installer.nix index 84fdb027ed85..9e5a6ad04e1e 100644 --- a/nixos/tests/installer.nix +++ b/nixos/tests/installer.nix @@ -46,7 +46,7 @@ let , grubIdentifier, preBootCommands, extraConfig }: let - iface = if grubVersion == 1 then "scsi" else "virtio"; + iface = if grubVersion == 1 then "ide" else "virtio"; qemuFlags = (if system == "x86_64-linux" then "-m 768 " else "-m 512 ") + (optionalString (system == "x86_64-linux") "-cpu kvm64 "); diff --git a/nixos/tests/misc.nix b/nixos/tests/misc.nix index 6297452df95e..73af0cfad21f 100644 --- a/nixos/tests/misc.nix +++ b/nixos/tests/misc.nix @@ -16,7 +16,7 @@ import ./make-test.nix ({ pkgs, ...} : { systemd.tmpfiles.rules = [ "d /tmp 1777 root root 10d" ]; fileSystems = mkVMOverride { "/tmp2" = { fsType = "tmpfs"; - options = "mode=1777,noauto"; + options = [ "mode=1777" "noauto" ]; }; }; systemd.automounts = singleton diff --git a/nixos/tests/networking.nix b/nixos/tests/networking.nix index 813d7c2bf516..d5a0a9b798f5 100644 --- a/nixos/tests/networking.nix +++ b/nixos/tests/networking.nix @@ -1,406 +1,411 @@ -import ./make-test.nix ({ pkgs, networkd, test, ... }: - let - router = { config, pkgs, ... }: - with pkgs.lib; - let - vlanIfs = range 1 (length config.virtualisation.vlans); - in { - virtualisation.vlans = [ 1 2 3 ]; +{ system ? builtins.currentSystem, networkd }: + +with import ../lib/testing.nix { inherit system; }; +with pkgs.lib; + +let + router = { config, pkgs, ... }: + with pkgs.lib; + let + vlanIfs = range 1 (length config.virtualisation.vlans); + in { + virtualisation.vlans = [ 1 2 3 ]; + networking = { + useDHCP = false; + useNetworkd = networkd; + firewall.allowPing = true; + interfaces = mkOverride 0 (listToAttrs (flip map vlanIfs (n: + nameValuePair "eth${toString n}" { + ipAddress = "192.168.${toString n}.1"; + prefixLength = 24; + }))); + }; + services.dhcpd = { + enable = true; + interfaces = map (n: "eth${toString n}") vlanIfs; + extraConfig = '' + option subnet-mask 255.255.255.0; + '' + flip concatMapStrings vlanIfs (n: '' + subnet 192.168.${toString n}.0 netmask 255.255.255.0 { + option broadcast-address 192.168.${toString n}.255; + option routers 192.168.${toString n}.1; + range 192.168.${toString n}.2 192.168.${toString n}.254; + } + ''); + }; + }; + + testCases = { + loopback = { + name = "Loopback"; + machine.networking.useNetworkd = networkd; + testScript = '' + startAll; + $machine->waitForUnit("network-interfaces.target"); + $machine->waitForUnit("network.target"); + $machine->succeed("ip addr show lo | grep -q 'inet 127.0.0.1/8 '"); + $machine->succeed("ip addr show lo | grep -q 'inet6 ::1/128 '"); + ''; + }; + static = { + name = "Static"; + nodes.router = router; + nodes.client = { config, pkgs, ... }: with pkgs.lib; { + virtualisation.vlans = [ 1 2 ]; networking = { - useDHCP = false; useNetworkd = networkd; firewall.allowPing = true; - interfaces = mkOverride 0 (listToAttrs (flip map vlanIfs (n: - nameValuePair "eth${toString n}" { - ipAddress = "192.168.${toString n}.1"; - prefixLength = 24; - }))); - }; - services.dhcpd = { - enable = true; - interfaces = map (n: "eth${toString n}") vlanIfs; - extraConfig = '' - option subnet-mask 255.255.255.0; - '' + flip concatMapStrings vlanIfs (n: '' - subnet 192.168.${toString n}.0 netmask 255.255.255.0 { - option broadcast-address 192.168.${toString n}.255; - option routers 192.168.${toString n}.1; - range 192.168.${toString n}.2 192.168.${toString n}.254; - } - ''); + useDHCP = false; + defaultGateway = "192.168.1.1"; + interfaces.eth1.ip4 = mkOverride 0 [ + { address = "192.168.1.2"; prefixLength = 24; } + { address = "192.168.1.3"; prefixLength = 32; } + { address = "192.168.1.10"; prefixLength = 32; } + ]; + interfaces.eth2.ip4 = mkOverride 0 [ + { address = "192.168.2.2"; prefixLength = 24; } + ]; }; }; - testCases = { - loopback = { - name = "Loopback"; - machine.networking.useNetworkd = networkd; - testScript = '' + testScript = { nodes, ... }: + '' startAll; - $machine->waitForUnit("network-interfaces.target"); - $machine->waitForUnit("network.target"); - $machine->succeed("ip addr show lo | grep -q 'inet 127.0.0.1/8 '"); - $machine->succeed("ip addr show lo | grep -q 'inet6 ::1/128 '"); + + $client->waitForUnit("network-interfaces.target"); + $client->waitForUnit("network.target"); + $router->waitForUnit("network-interfaces.target"); + $router->waitForUnit("network.target"); + + # Make sure dhcpcd is not started + $client->fail("systemctl status dhcpcd.service"); + + # Test vlan 1 + $client->waitUntilSucceeds("ping -c 1 192.168.1.1"); + $client->waitUntilSucceeds("ping -c 1 192.168.1.2"); + $client->waitUntilSucceeds("ping -c 1 192.168.1.3"); + $client->waitUntilSucceeds("ping -c 1 192.168.1.10"); + + $router->waitUntilSucceeds("ping -c 1 192.168.1.1"); + $router->waitUntilSucceeds("ping -c 1 192.168.1.2"); + $router->waitUntilSucceeds("ping -c 1 192.168.1.3"); + $router->waitUntilSucceeds("ping -c 1 192.168.1.10"); + + # Test vlan 2 + $client->waitUntilSucceeds("ping -c 1 192.168.2.1"); + $client->waitUntilSucceeds("ping -c 1 192.168.2.2"); + + $router->waitUntilSucceeds("ping -c 1 192.168.2.1"); + $router->waitUntilSucceeds("ping -c 1 192.168.2.2"); + + # Test default gateway + $router->waitUntilSucceeds("ping -c 1 192.168.3.1"); + $client->waitUntilSucceeds("ping -c 1 192.168.3.1"); ''; - }; - static = { - name = "Static"; - nodes.router = router; - nodes.client = { config, pkgs, ... }: with pkgs.lib; { - virtualisation.vlans = [ 1 2 ]; - networking = { - useNetworkd = networkd; - firewall.allowPing = true; - useDHCP = false; - defaultGateway = "192.168.1.1"; - interfaces.eth1.ip4 = mkOverride 0 [ - { address = "192.168.1.2"; prefixLength = 24; } - { address = "192.168.1.3"; prefixLength = 32; } - { address = "192.168.1.10"; prefixLength = 32; } - ]; - interfaces.eth2.ip4 = mkOverride 0 [ - { address = "192.168.2.2"; prefixLength = 24; } - ]; - }; + }; + dhcpSimple = { + name = "SimpleDHCP"; + nodes.router = router; + nodes.client = { config, pkgs, ... }: with pkgs.lib; { + virtualisation.vlans = [ 1 2 ]; + networking = { + useNetworkd = networkd; + firewall.allowPing = true; + useDHCP = true; + interfaces.eth1.ip4 = mkOverride 0 [ ]; + interfaces.eth2.ip4 = mkOverride 0 [ ]; }; - testScript = { nodes, ... }: - '' - startAll; - - $client->waitForUnit("network-interfaces.target"); - $client->waitForUnit("network.target"); - $router->waitForUnit("network-interfaces.target"); - $router->waitForUnit("network.target"); - - # Make sure dhcpcd is not started - $client->fail("systemctl status dhcpcd.service"); - - # Test vlan 1 - $client->waitUntilSucceeds("ping -c 1 192.168.1.1"); - $client->waitUntilSucceeds("ping -c 1 192.168.1.2"); - $client->waitUntilSucceeds("ping -c 1 192.168.1.3"); - $client->waitUntilSucceeds("ping -c 1 192.168.1.10"); - - $router->waitUntilSucceeds("ping -c 1 192.168.1.1"); - $router->waitUntilSucceeds("ping -c 1 192.168.1.2"); - $router->waitUntilSucceeds("ping -c 1 192.168.1.3"); - $router->waitUntilSucceeds("ping -c 1 192.168.1.10"); - - # Test vlan 2 - $client->waitUntilSucceeds("ping -c 1 192.168.2.1"); - $client->waitUntilSucceeds("ping -c 1 192.168.2.2"); - - $router->waitUntilSucceeds("ping -c 1 192.168.2.1"); - $router->waitUntilSucceeds("ping -c 1 192.168.2.2"); - - # Test default gateway - $router->waitUntilSucceeds("ping -c 1 192.168.3.1"); - $client->waitUntilSucceeds("ping -c 1 192.168.3.1"); - ''; }; - dhcpSimple = { - name = "SimpleDHCP"; - nodes.router = router; - nodes.client = { config, pkgs, ... }: with pkgs.lib; { - virtualisation.vlans = [ 1 2 ]; - networking = { - useNetworkd = networkd; - firewall.allowPing = true; - useDHCP = true; - interfaces.eth1.ip4 = mkOverride 0 [ ]; - interfaces.eth2.ip4 = mkOverride 0 [ ]; - }; - }; - testScript = { nodes, ... }: - '' - startAll; + testScript = { nodes, ... }: + '' + startAll; - $client->waitForUnit("network-interfaces.target"); - $client->waitForUnit("network.target"); - $router->waitForUnit("network-interfaces.target"); - $router->waitForUnit("network.target"); + $client->waitForUnit("network-interfaces.target"); + $client->waitForUnit("network.target"); + $router->waitForUnit("network-interfaces.target"); + $router->waitForUnit("network.target"); - # Wait until we have an ip address on each interface - $client->waitUntilSucceeds("ip addr show dev eth1 | grep -q '192.168.1'"); - $client->waitUntilSucceeds("ip addr show dev eth2 | grep -q '192.168.2'"); + # Wait until we have an ip address on each interface + $client->waitUntilSucceeds("ip addr show dev eth1 | grep -q '192.168.1'"); + $client->waitUntilSucceeds("ip addr show dev eth2 | grep -q '192.168.2'"); - # Test vlan 1 - $client->waitUntilSucceeds("ping -c 1 192.168.1.1"); - $client->waitUntilSucceeds("ping -c 1 192.168.1.2"); + # Test vlan 1 + $client->waitUntilSucceeds("ping -c 1 192.168.1.1"); + $client->waitUntilSucceeds("ping -c 1 192.168.1.2"); - $router->waitUntilSucceeds("ping -c 1 192.168.1.1"); - $router->waitUntilSucceeds("ping -c 1 192.168.1.2"); + $router->waitUntilSucceeds("ping -c 1 192.168.1.1"); + $router->waitUntilSucceeds("ping -c 1 192.168.1.2"); - # Test vlan 2 - $client->waitUntilSucceeds("ping -c 1 192.168.2.1"); - $client->waitUntilSucceeds("ping -c 1 192.168.2.2"); + # Test vlan 2 + $client->waitUntilSucceeds("ping -c 1 192.168.2.1"); + $client->waitUntilSucceeds("ping -c 1 192.168.2.2"); - $router->waitUntilSucceeds("ping -c 1 192.168.2.1"); - $router->waitUntilSucceeds("ping -c 1 192.168.2.2"); - ''; - }; - dhcpOneIf = { - name = "OneInterfaceDHCP"; - nodes.router = router; - nodes.client = { config, pkgs, ... }: with pkgs.lib; { - virtualisation.vlans = [ 1 2 ]; - networking = { - useNetworkd = networkd; - firewall.allowPing = true; - useDHCP = false; - interfaces.eth1 = { - ip4 = mkOverride 0 [ ]; - useDHCP = true; - }; - interfaces.eth2.ip4 = mkOverride 0 [ ]; + $router->waitUntilSucceeds("ping -c 1 192.168.2.1"); + $router->waitUntilSucceeds("ping -c 1 192.168.2.2"); + ''; + }; + dhcpOneIf = { + name = "OneInterfaceDHCP"; + nodes.router = router; + nodes.client = { config, pkgs, ... }: with pkgs.lib; { + virtualisation.vlans = [ 1 2 ]; + networking = { + useNetworkd = networkd; + firewall.allowPing = true; + useDHCP = false; + interfaces.eth1 = { + ip4 = mkOverride 0 [ ]; + useDHCP = true; }; + interfaces.eth2.ip4 = mkOverride 0 [ ]; }; - testScript = { nodes, ... }: - '' - startAll; + }; + testScript = { nodes, ... }: + '' + startAll; - # Wait for networking to come up - $client->waitForUnit("network-interfaces.target"); - $client->waitForUnit("network.target"); - $router->waitForUnit("network-interfaces.target"); - $router->waitForUnit("network.target"); + # Wait for networking to come up + $client->waitForUnit("network-interfaces.target"); + $client->waitForUnit("network.target"); + $router->waitForUnit("network-interfaces.target"); + $router->waitForUnit("network.target"); - # Wait until we have an ip address on each interface - $client->waitUntilSucceeds("ip addr show dev eth1 | grep -q '192.168.1'"); + # Wait until we have an ip address on each interface + $client->waitUntilSucceeds("ip addr show dev eth1 | grep -q '192.168.1'"); - # Test vlan 1 - $client->waitUntilSucceeds("ping -c 1 192.168.1.1"); - $client->waitUntilSucceeds("ping -c 1 192.168.1.2"); + # Test vlan 1 + $client->waitUntilSucceeds("ping -c 1 192.168.1.1"); + $client->waitUntilSucceeds("ping -c 1 192.168.1.2"); - $router->waitUntilSucceeds("ping -c 1 192.168.1.1"); - $router->waitUntilSucceeds("ping -c 1 192.168.1.2"); + $router->waitUntilSucceeds("ping -c 1 192.168.1.1"); + $router->waitUntilSucceeds("ping -c 1 192.168.1.2"); - # Test vlan 2 - $client->waitUntilSucceeds("ping -c 1 192.168.2.1"); - $client->fail("ping -c 1 192.168.2.2"); + # Test vlan 2 + $client->waitUntilSucceeds("ping -c 1 192.168.2.1"); + $client->fail("ping -c 1 192.168.2.2"); - $router->waitUntilSucceeds("ping -c 1 192.168.2.1"); - $router->fail("ping -c 1 192.168.2.2"); - ''; - }; - bond = let - node = address: { config, pkgs, ... }: with pkgs.lib; { - virtualisation.vlans = [ 1 2 ]; - networking = { - useNetworkd = networkd; - firewall.allowPing = true; - useDHCP = false; - bonds.bond = { - mode = "balance-rr"; - interfaces = [ "eth1" "eth2" ]; - }; - interfaces.eth1.ip4 = mkOverride 0 [ ]; - interfaces.eth2.ip4 = mkOverride 0 [ ]; - interfaces.bond.ip4 = mkOverride 0 - [ { inherit address; prefixLength = 30; } ]; + $router->waitUntilSucceeds("ping -c 1 192.168.2.1"); + $router->fail("ping -c 1 192.168.2.2"); + ''; + }; + bond = let + node = address: { config, pkgs, ... }: with pkgs.lib; { + virtualisation.vlans = [ 1 2 ]; + networking = { + useNetworkd = networkd; + firewall.allowPing = true; + useDHCP = false; + bonds.bond = { + mode = "balance-rr"; + interfaces = [ "eth1" "eth2" ]; }; + interfaces.eth1.ip4 = mkOverride 0 [ ]; + interfaces.eth2.ip4 = mkOverride 0 [ ]; + interfaces.bond.ip4 = mkOverride 0 + [ { inherit address; prefixLength = 30; } ]; }; - in { - name = "Bond"; - nodes.client1 = node "192.168.1.1"; - nodes.client2 = node "192.168.1.2"; - testScript = { nodes, ... }: - '' - startAll; - - # Wait for networking to come up - $client1->waitForUnit("network-interfaces.target"); - $client1->waitForUnit("network.target"); - $client2->waitForUnit("network-interfaces.target"); - $client2->waitForUnit("network.target"); - - # Test bonding - $client1->waitUntilSucceeds("ping -c 2 192.168.1.1"); - $client1->waitUntilSucceeds("ping -c 2 192.168.1.2"); - - $client2->waitUntilSucceeds("ping -c 2 192.168.1.1"); - $client2->waitUntilSucceeds("ping -c 2 192.168.1.2"); - ''; }; - bridge = let - node = { address, vlan }: { config, pkgs, ... }: with pkgs.lib; { - virtualisation.vlans = [ vlan ]; - networking = { - useNetworkd = networkd; - firewall.allowPing = true; - useDHCP = false; - interfaces.eth1.ip4 = mkOverride 0 - [ { inherit address; prefixLength = 24; } ]; - }; + in { + name = "Bond"; + nodes.client1 = node "192.168.1.1"; + nodes.client2 = node "192.168.1.2"; + testScript = { nodes, ... }: + '' + startAll; + + # Wait for networking to come up + $client1->waitForUnit("network-interfaces.target"); + $client1->waitForUnit("network.target"); + $client2->waitForUnit("network-interfaces.target"); + $client2->waitForUnit("network.target"); + + # Test bonding + $client1->waitUntilSucceeds("ping -c 2 192.168.1.1"); + $client1->waitUntilSucceeds("ping -c 2 192.168.1.2"); + + $client2->waitUntilSucceeds("ping -c 2 192.168.1.1"); + $client2->waitUntilSucceeds("ping -c 2 192.168.1.2"); + ''; + }; + bridge = let + node = { address, vlan }: { config, pkgs, ... }: with pkgs.lib; { + virtualisation.vlans = [ vlan ]; + networking = { + useNetworkd = networkd; + firewall.allowPing = true; + useDHCP = false; + interfaces.eth1.ip4 = mkOverride 0 + [ { inherit address; prefixLength = 24; } ]; }; - in { - name = "Bridge"; - nodes.client1 = node { address = "192.168.1.2"; vlan = 1; }; - nodes.client2 = node { address = "192.168.1.3"; vlan = 2; }; - nodes.router = { config, pkgs, ... }: with pkgs.lib; { - virtualisation.vlans = [ 1 2 ]; - networking = { - useNetworkd = networkd; - firewall.allowPing = true; - useDHCP = false; - bridges.bridge.interfaces = [ "eth1" "eth2" ]; - interfaces.eth1.ip4 = mkOverride 0 [ ]; - interfaces.eth2.ip4 = mkOverride 0 [ ]; - interfaces.bridge.ip4 = mkOverride 0 - [ { address = "192.168.1.1"; prefixLength = 24; } ]; - }; + }; + in { + name = "Bridge"; + nodes.client1 = node { address = "192.168.1.2"; vlan = 1; }; + nodes.client2 = node { address = "192.168.1.3"; vlan = 2; }; + nodes.router = { config, pkgs, ... }: with pkgs.lib; { + virtualisation.vlans = [ 1 2 ]; + networking = { + useNetworkd = networkd; + firewall.allowPing = true; + useDHCP = false; + bridges.bridge.interfaces = [ "eth1" "eth2" ]; + interfaces.eth1.ip4 = mkOverride 0 [ ]; + interfaces.eth2.ip4 = mkOverride 0 [ ]; + interfaces.bridge.ip4 = mkOverride 0 + [ { address = "192.168.1.1"; prefixLength = 24; } ]; }; - testScript = { nodes, ... }: - '' - startAll; - - # Wait for networking to come up - $client1->waitForUnit("network-interfaces.target"); - $client1->waitForUnit("network.target"); - $client2->waitForUnit("network-interfaces.target"); - $client2->waitForUnit("network.target"); - $router->waitForUnit("network-interfaces.target"); - $router->waitForUnit("network.target"); - - # Test bridging - $client1->waitUntilSucceeds("ping -c 1 192.168.1.1"); - $client1->waitUntilSucceeds("ping -c 1 192.168.1.2"); - $client1->waitUntilSucceeds("ping -c 1 192.168.1.3"); - - $client2->waitUntilSucceeds("ping -c 1 192.168.1.1"); - $client2->waitUntilSucceeds("ping -c 1 192.168.1.2"); - $client2->waitUntilSucceeds("ping -c 1 192.168.1.3"); - - $router->waitUntilSucceeds("ping -c 1 192.168.1.1"); - $router->waitUntilSucceeds("ping -c 1 192.168.1.2"); - $router->waitUntilSucceeds("ping -c 1 192.168.1.3"); - ''; }; - macvlan = { - name = "MACVLAN"; - nodes.router = router; - nodes.client = { config, pkgs, ... }: with pkgs.lib; { - virtualisation.vlans = [ 1 ]; - networking = { - useNetworkd = networkd; - firewall.allowPing = true; - useDHCP = true; - macvlans.macvlan.interface = "eth1"; - interfaces.eth1.ip4 = mkOverride 0 [ ]; - }; + testScript = { nodes, ... }: + '' + startAll; + + # Wait for networking to come up + $client1->waitForUnit("network-interfaces.target"); + $client1->waitForUnit("network.target"); + $client2->waitForUnit("network-interfaces.target"); + $client2->waitForUnit("network.target"); + $router->waitForUnit("network-interfaces.target"); + $router->waitForUnit("network.target"); + + # Test bridging + $client1->waitUntilSucceeds("ping -c 1 192.168.1.1"); + $client1->waitUntilSucceeds("ping -c 1 192.168.1.2"); + $client1->waitUntilSucceeds("ping -c 1 192.168.1.3"); + + $client2->waitUntilSucceeds("ping -c 1 192.168.1.1"); + $client2->waitUntilSucceeds("ping -c 1 192.168.1.2"); + $client2->waitUntilSucceeds("ping -c 1 192.168.1.3"); + + $router->waitUntilSucceeds("ping -c 1 192.168.1.1"); + $router->waitUntilSucceeds("ping -c 1 192.168.1.2"); + $router->waitUntilSucceeds("ping -c 1 192.168.1.3"); + ''; + }; + macvlan = { + name = "MACVLAN"; + nodes.router = router; + nodes.client = { config, pkgs, ... }: with pkgs.lib; { + virtualisation.vlans = [ 1 ]; + networking = { + useNetworkd = networkd; + firewall.allowPing = true; + useDHCP = true; + macvlans.macvlan.interface = "eth1"; + interfaces.eth1.ip4 = mkOverride 0 [ ]; }; - testScript = { nodes, ... }: - '' - startAll; - - # Wait for networking to come up - $client->waitForUnit("network-interfaces.target"); - $client->waitForUnit("network.target"); - $router->waitForUnit("network-interfaces.target"); - $router->waitForUnit("network.target"); - - # Wait until we have an ip address on each interface - $client->waitUntilSucceeds("ip addr show dev eth1 | grep -q '192.168.1'"); - $client->waitUntilSucceeds("ip addr show dev macvlan | grep -q '192.168.1'"); - - # Print diagnosting information - $router->succeed("ip addr >&2"); - $client->succeed("ip addr >&2"); - - # Test macvlan creates routable ips - $client->waitUntilSucceeds("ping -c 1 192.168.1.1"); - $client->waitUntilSucceeds("ping -c 1 192.168.1.2"); - $client->waitUntilSucceeds("ping -c 1 192.168.1.3"); - - $router->waitUntilSucceeds("ping -c 1 192.168.1.1"); - $router->waitUntilSucceeds("ping -c 1 192.168.1.2"); - $router->waitUntilSucceeds("ping -c 1 192.168.1.3"); - ''; }; - sit = let - node = { address4, remote, address6 }: { config, pkgs, ... }: with pkgs.lib; { - virtualisation.vlans = [ 1 ]; - networking = { - useNetworkd = networkd; - firewall.enable = false; - useDHCP = false; - sits.sit = { - inherit remote; - local = address4; - dev = "eth1"; - }; - interfaces.eth1.ip4 = mkOverride 0 - [ { address = address4; prefixLength = 24; } ]; - interfaces.sit.ip6 = mkOverride 0 - [ { address = address6; prefixLength = 64; } ]; + testScript = { nodes, ... }: + '' + startAll; + + # Wait for networking to come up + $client->waitForUnit("network-interfaces.target"); + $client->waitForUnit("network.target"); + $router->waitForUnit("network-interfaces.target"); + $router->waitForUnit("network.target"); + + # Wait until we have an ip address on each interface + $client->waitUntilSucceeds("ip addr show dev eth1 | grep -q '192.168.1'"); + $client->waitUntilSucceeds("ip addr show dev macvlan | grep -q '192.168.1'"); + + # Print diagnosting information + $router->succeed("ip addr >&2"); + $client->succeed("ip addr >&2"); + + # Test macvlan creates routable ips + $client->waitUntilSucceeds("ping -c 1 192.168.1.1"); + $client->waitUntilSucceeds("ping -c 1 192.168.1.2"); + $client->waitUntilSucceeds("ping -c 1 192.168.1.3"); + + $router->waitUntilSucceeds("ping -c 1 192.168.1.1"); + $router->waitUntilSucceeds("ping -c 1 192.168.1.2"); + $router->waitUntilSucceeds("ping -c 1 192.168.1.3"); + ''; + }; + sit = let + node = { address4, remote, address6 }: { config, pkgs, ... }: with pkgs.lib; { + virtualisation.vlans = [ 1 ]; + networking = { + useNetworkd = networkd; + firewall.enable = false; + useDHCP = false; + sits.sit = { + inherit remote; + local = address4; + dev = "eth1"; }; + interfaces.eth1.ip4 = mkOverride 0 + [ { address = address4; prefixLength = 24; } ]; + interfaces.sit.ip6 = mkOverride 0 + [ { address = address6; prefixLength = 64; } ]; }; - in { - name = "Sit"; - nodes.client1 = node { address4 = "192.168.1.1"; remote = "192.168.1.2"; address6 = "fc00::1"; }; - nodes.client2 = node { address4 = "192.168.1.2"; remote = "192.168.1.1"; address6 = "fc00::2"; }; - testScript = { nodes, ... }: - '' - startAll; - - # Wait for networking to be configured - $client1->waitForUnit("network-interfaces.target"); - $client1->waitForUnit("network.target"); - $client2->waitForUnit("network-interfaces.target"); - $client2->waitForUnit("network.target"); - - # Print diagnostic information - $client1->succeed("ip addr >&2"); - $client2->succeed("ip addr >&2"); - - # Test ipv6 - $client1->waitUntilSucceeds("ping6 -c 1 fc00::1"); - $client1->waitUntilSucceeds("ping6 -c 1 fc00::2"); - - $client2->waitUntilSucceeds("ping6 -c 1 fc00::1"); - $client2->waitUntilSucceeds("ping6 -c 1 fc00::2"); - ''; }; - vlan = let - node = address: { config, pkgs, ... }: with pkgs.lib; { - #virtualisation.vlans = [ 1 ]; - networking = { - useNetworkd = networkd; - firewall.allowPing = true; - useDHCP = false; - vlans.vlan = { - id = 1; - interface = "eth0"; - }; - interfaces.eth0.ip4 = mkOverride 0 [ ]; - interfaces.eth1.ip4 = mkOverride 0 [ ]; - interfaces.vlan.ip4 = mkOverride 0 - [ { inherit address; prefixLength = 24; } ]; + in { + name = "Sit"; + nodes.client1 = node { address4 = "192.168.1.1"; remote = "192.168.1.2"; address6 = "fc00::1"; }; + nodes.client2 = node { address4 = "192.168.1.2"; remote = "192.168.1.1"; address6 = "fc00::2"; }; + testScript = { nodes, ... }: + '' + startAll; + + # Wait for networking to be configured + $client1->waitForUnit("network-interfaces.target"); + $client1->waitForUnit("network.target"); + $client2->waitForUnit("network-interfaces.target"); + $client2->waitForUnit("network.target"); + + # Print diagnostic information + $client1->succeed("ip addr >&2"); + $client2->succeed("ip addr >&2"); + + # Test ipv6 + $client1->waitUntilSucceeds("ping6 -c 1 fc00::1"); + $client1->waitUntilSucceeds("ping6 -c 1 fc00::2"); + + $client2->waitUntilSucceeds("ping6 -c 1 fc00::1"); + $client2->waitUntilSucceeds("ping6 -c 1 fc00::2"); + ''; + }; + vlan = let + node = address: { config, pkgs, ... }: with pkgs.lib; { + #virtualisation.vlans = [ 1 ]; + networking = { + useNetworkd = networkd; + firewall.allowPing = true; + useDHCP = false; + vlans.vlan = { + id = 1; + interface = "eth0"; }; + interfaces.eth0.ip4 = mkOverride 0 [ ]; + interfaces.eth1.ip4 = mkOverride 0 [ ]; + interfaces.vlan.ip4 = mkOverride 0 + [ { inherit address; prefixLength = 24; } ]; }; - in { - name = "vlan"; - nodes.client1 = node "192.168.1.1"; - nodes.client2 = node "192.168.1.2"; - testScript = { nodes, ... }: - '' - startAll; - - # Wait for networking to be configured - $client1->waitForUnit("network-interfaces.target"); - $client1->waitForUnit("network.target"); - $client2->waitForUnit("network-interfaces.target"); - $client2->waitForUnit("network.target"); - - # Test vlan is setup - $client1->succeed("ip addr show dev vlan >&2"); - $client2->succeed("ip addr show dev vlan >&2"); - ''; }; + in { + name = "vlan"; + nodes.client1 = node "192.168.1.1"; + nodes.client2 = node "192.168.1.2"; + testScript = { nodes, ... }: + '' + startAll; + + # Wait for networking to be configured + $client1->waitForUnit("network-interfaces.target"); + $client1->waitForUnit("network.target"); + $client2->waitForUnit("network-interfaces.target"); + $client2->waitForUnit("network.target"); + + # Test vlan is setup + $client1->succeed("ip addr show dev vlan >&2"); + $client2->succeed("ip addr show dev vlan >&2"); + ''; }; - case = testCases.${test}; - in case // { - name = "${case.name}-Networking-${if networkd then "Networkd" else "Scripted"}"; - meta = with pkgs.stdenv.lib.maintainers; { - maintainers = [ wkennington ]; - }; - }) + }; + +in mapAttrs (const (attrs: makeTest (attrs // { + name = "${attrs.name}-Networking-${if networkd then "Networkd" else "Scripted"}"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ wkennington ]; + }; +}))) testCases diff --git a/nixos/tests/nfs.nix b/nixos/tests/nfs.nix index 24f6e0f2ed95..36cd6a395779 100644 --- a/nixos/tests/nfs.nix +++ b/nixos/tests/nfs.nix @@ -8,7 +8,7 @@ let [ { mountPoint = "/data"; device = "server:/data"; fsType = "nfs"; - options = "vers=${toString version}"; + options = [ "vers=${toString version}" ]; } ]; networking.firewall.enable = false; # FIXME: only open statd diff --git a/nixos/tests/nixos-pin-version.nix b/nixos/tests/nixos-pin-version.nix deleted file mode 100644 index 91fba2e759d2..000000000000 --- a/nixos/tests/nixos-pin-version.nix +++ /dev/null @@ -1,57 +0,0 @@ -{ system ? builtins.currentSystem }: - -with import ../lib/testing.nix { inherit system; }; -let -in - -pkgs.stdenv.mkDerivation rec { - name = "nixos-pin-version"; - src = ../..; - buildInputs = with pkgs; [ nix gnugrep ]; - - withoutPath = pkgs.writeText "configuration.nix" '' - { - nixos.extraModules = [ ({lib, ...}: { system.nixosRevision = lib.mkForce "ABCDEF"; }) ]; - } - ''; - - withPath = pkgs.writeText "configuration.nix" '' - { - nixos.path = ${src}/nixos ; - nixos.extraModules = [ ({lib, ...}: { system.nixosRevision = lib.mkForce "ABCDEF"; }) ]; - } - ''; - - phases = "buildPhase"; - buildPhase = '' - datadir="${pkgs.nix}/share" - export TEST_ROOT=$(pwd)/test-tmp - export NIX_STORE_DIR=$TEST_ROOT/store - export NIX_LOCALSTATE_DIR=$TEST_ROOT/var - export NIX_LOG_DIR=$TEST_ROOT/var/log/nix - export NIX_STATE_DIR=$TEST_ROOT/var/nix - export NIX_DB_DIR=$TEST_ROOT/db - export NIX_CONF_DIR=$TEST_ROOT/etc - export NIX_MANIFESTS_DIR=$TEST_ROOT/var/nix/manifests - export NIX_BUILD_HOOK= - export PAGER=cat - cacheDir=$TEST_ROOT/binary-cache - nix-store --init - - export NIX_PATH="nixpkgs=$src:nixos=$src/nixos:nixos-config=${withoutPath}" ; - if test $(nix-instantiate $src/nixos -A config.system.nixosRevision --eval-only) != '"ABCDEF"' ; then :; - else - echo "Unexpected re-entry without the nixos.path option defined."; - exit 1; - fi; - - export NIX_PATH="nixpkgs=$src:nixos=$src/nixos:nixos-config=${withPath}" ; - if test $(nix-instantiate $src/nixos -A config.system.nixosRevision --eval-only) = '"ABCDEF"' ; then :; - else - echo "Expected a re-entry when the nixos.path option is defined."; - exit 1; - fi; - - touch $out; - ''; -} diff --git a/nixos/tests/postgresql.nix b/nixos/tests/postgresql.nix new file mode 100644 index 000000000000..f17384b44ba6 --- /dev/null +++ b/nixos/tests/postgresql.nix @@ -0,0 +1,26 @@ +import ./make-test.nix ({ pkgs, ...} : { + name = "postgresql"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ zagy ]; + }; + + nodes = { + master = + { pkgs, config, ... }: + + { + services.postgresql.enable = true; + services.postgresql.initialScript = pkgs.writeText "postgresql-init.sql" + '' + CREATE ROLE postgres WITH superuser login createdb; + ''; + }; + }; + + testScript = '' + startAll; + $master->waitForUnit("postgresql"); + $master->sleep(10); # Hopefully this is long enough!! + $master->succeed("echo 'select 1' | sudo -u postgres psql"); + ''; +}) diff --git a/nixos/tests/printing.nix b/nixos/tests/printing.nix index 02980cee2fbd..10d69b446cd7 100644 --- a/nixos/tests/printing.nix +++ b/nixos/tests/printing.nix @@ -78,7 +78,7 @@ import ./make-test.nix ({pkgs, ... }: { # (showing that the right filters have been applied). Of # course, since there is no actual USB printer attached, the # file will stay in the queue forever. - $server->waitForFile("/var/spool/cups/d00001-001"); + $server->waitForFile("/var/spool/cups/d*-001"); $server->sleep(10); $server->succeed("lpq -a") =~ /$fn/ or die; @@ -90,6 +90,9 @@ import ./make-test.nix ({pkgs, ... }: { Machine::retry sub { return 1 if $server->succeed("lpq -a") =~ /no entries/; }; + # The queue is empty already, so this should be safe. + # Otherwise, pairs of "c*"-"d*-001" files might persist. + $server->execute("rm /var/spool/cups/*"); }; } ''; diff --git a/nixos/tests/simple.nix b/nixos/tests/simple.nix index 287712be316d..04d624adcfe9 100644 --- a/nixos/tests/simple.nix +++ b/nixos/tests/simple.nix @@ -4,7 +4,9 @@ import ./make-test.nix ({ pkgs, ...} : { maintainers = [ eelco ]; }; - machine = { config, pkgs, ... }: { }; + machine = { config, pkgs, ... }: { + imports = [ ../modules/profiles/minimal.nix ]; + }; testScript = '' diff --git a/nixos/tests/virtualbox.nix b/nixos/tests/virtualbox.nix index 01fcd15fd8bb..da4c0bddc348 100644 --- a/nixos/tests/virtualbox.nix +++ b/nixos/tests/virtualbox.nix @@ -1,7 +1,9 @@ -{ debug ? false, ... } @ args: +{ system ? builtins.currentSystem, debug ? false }: -import ./make-test.nix ({ pkgs, ... }: with pkgs.lib; let +with import ../lib/testing.nix { inherit system; }; +with pkgs.lib; +let testVMConfig = vmName: attrs: { config, pkgs, ... }: let guestAdditions = pkgs.linuxPackages.virtualboxGuestAdditions; @@ -314,138 +316,140 @@ import ./make-test.nix ({ pkgs, ... }: with pkgs.lib; let test2.vmScript = dhcpScript; }; -in { - name = "virtualbox"; - meta = with pkgs.stdenv.lib.maintainers; { - maintainers = [ aszlig wkennington ]; - }; + mkVBoxTest = name: testScript: makeTest { + name = "virtualbox-${name}"; + + machine = { lib, config, ... }: { + imports = let + mkVMConf = name: val: val.machine // { key = "${name}-config"; }; + vmConfigs = mapAttrsToList mkVMConf vboxVMs; + in [ ./common/user-account.nix ./common/x11.nix ] ++ vmConfigs; + virtualisation.memorySize = 2048; + virtualisation.virtualbox.host.enable = true; + users.extraUsers.alice.extraGroups = let + inherit (config.virtualisation.virtualbox.host) enableHardening; + in lib.mkIf enableHardening (lib.singleton "vboxusers"); + }; - machine = { pkgs, lib, config, ... }: { - imports = let - mkVMConf = name: val: val.machine // { key = "${name}-config"; }; - vmConfigs = mapAttrsToList mkVMConf vboxVMs; - in [ ./common/user-account.nix ./common/x11.nix ] ++ vmConfigs; - virtualisation.memorySize = 2048; - virtualisation.virtualbox.host.enable = true; - users.extraUsers.alice.extraGroups = let - inherit (config.virtualisation.virtualbox.host) enableHardening; - in lib.mkIf enableHardening (lib.singleton "vboxusers"); - }; + testScript = '' + sub ru ($) { + my $esc = $_[0] =~ s/'/'\\${"'"}'/gr; + return "su - alice -c '$esc'"; + } - testScript = '' - sub ru ($) { - my $esc = $_[0] =~ s/'/'\\${"'"}'/gr; - return "su - alice -c '$esc'"; - } + sub vbm { + $machine->succeed(ru("VBoxManage ".$_[0])); + }; - sub vbm { - $machine->succeed(ru("VBoxManage ".$_[0])); - }; + sub removeUUIDs { + return join("\n", grep { $_ !~ /^UUID:/ } split(/\n/, $_[0]))."\n"; + } - ${concatStrings (mapAttrsToList (_: getAttr "testSubs") vboxVMs)} + ${concatStrings (mapAttrsToList (_: getAttr "testSubs") vboxVMs)} - $machine->waitForX; + $machine->waitForX; - ${mkLog "$HOME/.config/VirtualBox/VBoxSVC.log" "HOST-SVC"} + ${mkLog "$HOME/.config/VirtualBox/VBoxSVC.log" "HOST-SVC"} - createVM_simple; + ${testScript} + ''; - subtest "simple-gui", sub { - $machine->succeed(ru "VirtualBox &"); - $machine->waitForWindow(qr/Oracle VM VirtualBox Manager/); - $machine->sleep(5); - $machine->screenshot("gui_manager_started"); - $machine->sendKeys("ret"); - $machine->screenshot("gui_manager_sent_startup"); - waitForStartup_simple (sub { - $machine->sendKeys("ret"); - }); - $machine->screenshot("gui_started"); - waitForVMBoot_simple; - $machine->screenshot("gui_booted"); - shutdownVM_simple; - $machine->sleep(5); - $machine->screenshot("gui_stopped"); - $machine->sendKeys("ctrl-q"); - $machine->sleep(5); - $machine->screenshot("gui_manager_stopped"); + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ aszlig wkennington ]; }; + }; - cleanup_simple; +in mapAttrs mkVBoxTest { + simple-gui = '' + createVM_simple; + $machine->succeed(ru "VirtualBox &"); + $machine->waitForWindow(qr/Oracle VM VirtualBox Manager/); + $machine->sleep(5); + $machine->screenshot("gui_manager_started"); + $machine->sendKeys("ret"); + $machine->screenshot("gui_manager_sent_startup"); + waitForStartup_simple (sub { + $machine->sendKeys("ret"); + }); + $machine->screenshot("gui_started"); + waitForVMBoot_simple; + $machine->screenshot("gui_booted"); + shutdownVM_simple; + $machine->sleep(5); + $machine->screenshot("gui_stopped"); + $machine->sendKeys("ctrl-q"); + $machine->sleep(5); + $machine->screenshot("gui_manager_stopped"); + ''; - subtest "simple-cli", sub { - vbm("startvm simple"); - waitForStartup_simple; - $machine->screenshot("cli_started"); - waitForVMBoot_simple; - $machine->screenshot("cli_booted"); - shutdownVM_simple; - }; + simple-cli = '' + createVM_simple; + vbm("startvm simple"); + waitForStartup_simple; + $machine->screenshot("cli_started"); + waitForVMBoot_simple; + $machine->screenshot("cli_booted"); - subtest "privilege-escalation", sub { + $machine->nest("Checking for privilege escalation", sub { $machine->fail("test -e '/root/VirtualBox VMs'"); $machine->fail("test -e '/root/.config/VirtualBox'"); $machine->succeed("test -e '/home/alice/VirtualBox VMs'"); - }; - - destroyVM_simple; + }); - sub removeUUIDs { - return join("\n", grep { $_ !~ /^UUID:/ } split(/\n/, $_[0]))."\n"; - } + shutdownVM_simple; + ''; - subtest "host-usb-permissions", sub { - my $userUSB = removeUUIDs vbm("list usbhost"); - print STDERR $userUSB; - my $rootUSB = removeUUIDs $machine->succeed("VBoxManage list usbhost"); - print STDERR $rootUSB; + host-usb-permissions = '' + my $userUSB = removeUUIDs vbm("list usbhost"); + print STDERR $userUSB; + my $rootUSB = removeUUIDs $machine->succeed("VBoxManage list usbhost"); + print STDERR $rootUSB; - die "USB host devices differ for root and normal user" - if $userUSB ne $rootUSB; - die "No USB host devices found" if $userUSB =~ /<none>/; - }; + die "USB host devices differ for root and normal user" + if $userUSB ne $rootUSB; + die "No USB host devices found" if $userUSB =~ /<none>/; + ''; - subtest "systemd-detect-virt", sub { - createVM_detectvirt; - vbm("startvm detectvirt"); - waitForStartup_detectvirt; - waitForVMBoot_detectvirt; - shutdownVM_detectvirt; - my $result = $machine->succeed("cat '$detectvirt_sharepath/result'"); - chomp $result; - destroyVM_detectvirt; - die "systemd-detect-virt returned \"$result\" instead of \"oracle\"" - if $result ne "oracle"; - }; + systemd-detect-virt = '' + createVM_detectvirt; + vbm("startvm detectvirt"); + waitForStartup_detectvirt; + waitForVMBoot_detectvirt; + shutdownVM_detectvirt; + my $result = $machine->succeed("cat '$detectvirt_sharepath/result'"); + chomp $result; + destroyVM_detectvirt; + die "systemd-detect-virt returned \"$result\" instead of \"oracle\"" + if $result ne "oracle"; + ''; - subtest "net-hostonlyif", sub { - createVM_test1; - createVM_test2; + net-hostonlyif = '' + createVM_test1; + createVM_test2; - vbm("startvm test1"); - waitForStartup_test1; - waitForVMBoot_test1; + vbm("startvm test1"); + waitForStartup_test1; + waitForVMBoot_test1; - vbm("startvm test2"); - waitForStartup_test2; - waitForVMBoot_test2; + vbm("startvm test2"); + waitForStartup_test2; + waitForVMBoot_test2; - $machine->screenshot("net_booted"); + $machine->screenshot("net_booted"); - my $test1IP = waitForIP_test1 1; - my $test2IP = waitForIP_test2 1; + my $test1IP = waitForIP_test1 1; + my $test2IP = waitForIP_test2 1; - $machine->succeed("echo '$test2IP' | netcat -c '$test1IP' 1234"); - $machine->succeed("echo '$test1IP' | netcat -c '$test2IP' 1234"); + $machine->succeed("echo '$test2IP' | netcat -c '$test1IP' 1234"); + $machine->succeed("echo '$test1IP' | netcat -c '$test2IP' 1234"); - $machine->waitUntilSucceeds("netcat -c '$test1IP' 5678 >&2"); - $machine->waitUntilSucceeds("netcat -c '$test2IP' 5678 >&2"); + $machine->waitUntilSucceeds("netcat -c '$test1IP' 5678 >&2"); + $machine->waitUntilSucceeds("netcat -c '$test2IP' 5678 >&2"); - shutdownVM_test1; - shutdownVM_test2; + shutdownVM_test1; + shutdownVM_test2; - destroyVM_test1; - destroyVM_test2; - }; + destroyVM_test1; + destroyVM_test2; ''; -}) args +} |