diff options
Diffstat (limited to 'nixos')
94 files changed, 3397 insertions, 854 deletions
diff --git a/nixos/doc/manual/Makefile b/nixos/doc/manual/Makefile index 2e9adf70c396..b251a1f5e2c3 100644 --- a/nixos/doc/manual/Makefile +++ b/nixos/doc/manual/Makefile @@ -4,7 +4,7 @@ all: manual-combined.xml format .PHONY: debug debug: generated manual-combined.xml -manual-combined.xml: generated *.xml +manual-combined.xml: generated *.xml **/*.xml rm -f ./manual-combined.xml nix-shell --packages xmloscopy \ --run "xmloscopy --docbook5 ./manual.xml ./manual-combined.xml" diff --git a/nixos/doc/manual/administration/container-networking.xml b/nixos/doc/manual/administration/container-networking.xml index 4b977d1d82eb..8aca329c8f1f 100644 --- a/nixos/doc/manual/administration/container-networking.xml +++ b/nixos/doc/manual/administration/container-networking.xml @@ -52,4 +52,7 @@ $ ping -c1 10.233.4.2 networking.networkmanager.unmanaged = [ "interface-name:ve-*" ]; </programlisting> </para> + <para> + You may need to restart your system for the changes to take effect. + </para> </section> diff --git a/nixos/doc/manual/administration/imperative-containers.xml b/nixos/doc/manual/administration/imperative-containers.xml index fa380477f6cb..9bb62bc2ece9 100644 --- a/nixos/doc/manual/administration/imperative-containers.xml +++ b/nixos/doc/manual/administration/imperative-containers.xml @@ -73,7 +73,8 @@ Linux foo 3.4.82 #1-NixOS SMP Thu Mar 20 14:44:05 UTC 2014 x86_64 GNU/Linux </para> <para> - To change the configuration of the container, you can edit + There are several ways to change the configuration of the container. First, + on the host, you can edit <literal>/var/lib/container/<replaceable>name</replaceable>/etc/nixos/configuration.nix</literal>, and run <screen> @@ -86,7 +87,8 @@ Linux foo 3.4.82 #1-NixOS SMP Thu Mar 20 14:44:05 UTC 2014 x86_64 GNU/Linux <xref linkend="opt-services.httpd.enable"/> = true; <xref linkend="opt-services.httpd.adminAddr"/> = "foo@example.org"; <xref linkend="opt-networking.firewall.allowedTCPPorts"/> = [ 80 ]; - ' +' + # curl http://$(nixos-container show-ip foo)/ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">… </screen> @@ -95,13 +97,11 @@ Linux foo 3.4.82 #1-NixOS SMP Thu Mar 20 14:44:05 UTC 2014 x86_64 GNU/Linux </para> <para> - Note that in previous versions of NixOS (17.09 and earlier) one could also - use all nix-related commands (like <command>nixos-rebuild switch</command>) - from inside the container. However, since the release of Nix 2.0 this is not - supported anymore. Supporting Nix commands inside the container might be - possible again in future versions. See - <link xlink:href="https://github.com/NixOS/nixpkgs/issues/40355">the github - issue</link> for tracking progress on this issue. + Alternatively, you can change the configuration from within the container + itself by running <command>nixos-rebuild switch</command> inside the + container. Note that the container by default does not have a copy of the + NixOS channel, so you should run <command>nix-channel --update</command> + first. </para> <para> diff --git a/nixos/doc/manual/installation/installing-usb.xml b/nixos/doc/manual/installation/installing-usb.xml index c5934111749c..0b311189430c 100644 --- a/nixos/doc/manual/installation/installing-usb.xml +++ b/nixos/doc/manual/installation/installing-usb.xml @@ -9,13 +9,12 @@ For systems without CD drive, the NixOS live CD can be booted from a USB stick. You can use the <command>dd</command> utility to write the image: <command>dd if=<replaceable>path-to-image</replaceable> - of=<replaceable>/dev/sdb</replaceable></command>. Be careful about specifying + of=<replaceable>/dev/sdX</replaceable></command>. Be careful about specifying the correct drive; you can use the <command>lsblk</command> command to get a list of block devices. - </para> - - <para> - On macOS: + <note> + <title>On macOS</title> + <para> <programlisting> $ diskutil list [..] @@ -26,43 +25,16 @@ $ diskutil unmountDisk diskN Unmount of all volumes on diskN was successful $ sudo dd bs=1m if=nix.iso of=/dev/rdiskN </programlisting> - Using the 'raw' <command>rdiskN</command> device instead of - <command>diskN</command> completes in minutes instead of hours. After - <command>dd</command> completes, a GUI dialog "The disk you inserted was not - readable by this computer" will pop up, which can be ignored. + Using the 'raw' <command>rdiskN</command> device instead of + <command>diskN</command> completes in minutes instead of hours. After + <command>dd</command> completes, a GUI dialog "The disk you inserted was + not readable by this computer" will pop up, which can be ignored. + </para> + </note> </para> <para> The <command>dd</command> utility will write the image verbatim to the drive, making it the recommended option for both UEFI and non-UEFI installations. - For non-UEFI installations, you can alternatively use - <link xlink:href="http://unetbootin.sourceforge.net/">unetbootin</link>. If - you cannot use <command>dd</command> for a UEFI installation, you can also - mount the ISO, copy its contents verbatim to your drive, then either: - <itemizedlist> - <listitem> - <para> - Change the label of the disk partition to the label of the ISO (visible - with the blkid command), or - </para> - </listitem> - <listitem> - <para> - Edit <filename>loader/entries/nixos-livecd.conf</filename> on the drive - and change the <literal>root=</literal> field in the - <literal>options</literal> line to point to your drive (see the - documentation on <literal>root=</literal> in - <link xlink:href="https://www.kernel.org/doc/Documentation/admin-guide/kernel-parameters.txt"> - the kernel documentation</link> for more details). - </para> - </listitem> - <listitem> - <para> - If you want to load the contents of the ISO to ram after bootin (So you - can remove the stick after bootup) you can append the parameter - <literal>copytoram</literal> to the <literal>options</literal> field. - </para> - </listitem> - </itemizedlist> </para> </section> diff --git a/nixos/doc/manual/installation/installing.xml b/nixos/doc/manual/installation/installing.xml index 1366e8f93596..8e94f946c5ee 100644 --- a/nixos/doc/manual/installation/installing.xml +++ b/nixos/doc/manual/installation/installing.xml @@ -4,60 +4,46 @@ version="5.0" xml:id="sec-installation"> <title>Installing NixOS</title> - <para> - NixOS can be installed on BIOS or UEFI systems. The procedure for a UEFI - installation is by and large the same as a BIOS installation. The differences - are mentioned in the steps that follow. - </para> - <orderedlist> - <listitem> - <para> - Boot from the CD. - </para> - <variablelist> - <varlistentry> - <term> - UEFI systems - </term> - <listitem> - <para> - You should boot the live CD in UEFI mode (consult your specific - hardware's documentation for instructions). You may find the - <link xlink:href="http://www.rodsbooks.com/refind">rEFInd boot - manager</link> useful. - </para> - </listitem> - </varlistentry> - </variablelist> - </listitem> - <listitem> - <para> - The CD contains a basic NixOS installation. (It also contains Memtest86+, - useful if you want to test new hardware). When it’s finished booting, it - should have detected most of your hardware. - </para> - </listitem> - <listitem> - <para> - The NixOS manual is available on virtual console 8 (press Alt+F8 to access) - or by running <command>nixos-help</command>. - </para> - </listitem> - <listitem> - <para> - You get logged in as <literal>root</literal> (with empty password). - </para> - </listitem> - <listitem> - <para> - If you downloaded the graphical ISO image, you can run <command>systemctl - 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> + <section xml:id="sec-installation-booting"> + <title>Booting the system</title> + + <para> + NixOS can be installed on BIOS or UEFI systems. The procedure for a UEFI + installation is by and large the same as a BIOS installation. The + differences are mentioned in the steps that follow. + </para> + + <para> + The installation media can be burned to a CD, or now more commonly, "burned" + to a USB drive (see <xref linkend="sec-booting-from-usb"/>). + </para> + + <para> + The installation media contains a basic NixOS installation. When it’s + finished booting, it should have detected most of your hardware. + </para> + + <para> + The NixOS manual is available on virtual console 8 (press Alt+F8 to access) + or by running <command>nixos-help</command>. + </para> + + <para> + You are logged-in automatically as <literal>root</literal>. (The + <literal>root</literal> user account has an empty password.) + </para> + + <para> + If you downloaded the graphical ISO image, you can run <command>systemctl + 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> + + <section xml:id="sec-installation-booting-networking"> + <title>Networking in the installer</title> + <para> The boot process should have brought up networking (check <command>ip a</command>). Networking is necessary for the installer, since it will @@ -65,60 +51,167 @@ binaries). It’s best if you have a DHCP server on your network. Otherwise configure networking manually using <command>ifconfig</command>. </para> + <para> To manually configure the network on the graphical installer, first disable network-manager with <command>systemctl stop network-manager</command>. </para> + <para> To manually configure the wifi on the minimal installer, run <command>wpa_supplicant -B -i interface -c <(wpa_passphrase 'SSID' 'key')</command>. </para> - </listitem> - <listitem> + <para> If you would like to continue the installation from a different machine you need to activate the SSH daemon via <literal>systemctl start sshd</literal>. In order to be able to login you also need to set a password for <literal>root</literal> using <literal>passwd</literal>. </para> - </listitem> - <listitem> + </section> + </section> + <section xml:id="sec-installation-partitioning"> + <title>Partitioning and formatting</title> + + <para> + The NixOS installer doesn’t do any partitioning or formatting, so you need + to do that yourself. + </para> + + <para> + The NixOS installer ships with multiple partitioning tools. The examples + below use <command>parted</command>, but also provides + <command>fdisk</command>, <command>gdisk</command>, + <command>cfdisk</command>, and <command>cgdisk</command>. + </para> + + <para> + The recommended partition scheme differs depending if the computer uses + <emphasis>Legacy Boot</emphasis> or <emphasis>UEFI</emphasis>. + </para> + + <section xml:id="sec-installation-partitioning-UEFI"> + <title>UEFI (GPT)</title> + <para> - The NixOS installer doesn’t do any partitioning or formatting yet, so you - need to do that yourself. Use the following commands: - <itemizedlist> + Here's an example partition scheme for UEFI, using + <filename>/dev/sda</filename> as the device. + <note> + <para> + You can safely ignore <command>parted</command>'s informational message + about needing to update /etc/fstab. + </para> + </note> + </para> + + <para> + <orderedlist> <listitem> <para> - For partitioning: <command>fdisk</command>. -<screen> -# fdisk /dev/sda # <lineannotation>(or whatever device you want to install on)</lineannotation> --- for UEFI systems only -> n # <lineannotation>(create a new partition for /boot)</lineannotation> -> 3 # <lineannotation>(make it a partition number 3)</lineannotation> -> # <lineannotation>(press enter to accept the default)</lineannotation> -> +512M # <lineannotation>(the size of the UEFI boot partition)</lineannotation> -> t # <lineannotation>(change the partition type ...)</lineannotation> -> 3 # <lineannotation>(... of the boot partition ...)</lineannotation> -> 1 # <lineannotation>(... to 'UEFI System')</lineannotation> --- for BIOS or UEFI systems -> n # <lineannotation>(create a new partition for /swap)</lineannotation> -> 2 # <lineannotation>(make it a partition number 2)</lineannotation> -> # <lineannotation>(press enter to accept the default)</lineannotation> -> +8G # <lineannotation>(the size of the swap partition, set to whatever you like)</lineannotation> -> n # <lineannotation>(create a new partition for /)</lineannotation> -> 1 # <lineannotation>(make it a partition number 1)</lineannotation> -> # <lineannotation>(press enter to accept the default)</lineannotation> -> # <lineannotation>(press enter to accept the default and use the rest of the remaining space)</lineannotation> -> a # <lineannotation>(make the partition bootable)</lineannotation> -> x # <lineannotation>(enter expert mode)</lineannotation> -> f # <lineannotation>(fix up the partition ordering)</lineannotation> -> r # <lineannotation>(exit expert mode)</lineannotation> -> w # <lineannotation>(write the partition table to disk and exit)</lineannotation></screen> + Create a <emphasis>GPT</emphasis> partition table. +<screen language="commands"># parted /dev/sda -- mklabel gpt</screen> </para> </listitem> <listitem> <para> + Add the <emphasis>root</emphasis> partition. This will fill the disk + except for the end part, where the swap will live, and the space left in + front (512MiB) which will be used by the boot partition. +<screen language="commands"># parted /dev/sda -- mkpart primary 512MiB -8GiB</screen> + </para> + </listitem> + <listitem> + <para> + Next, add a <emphasis>swap</emphasis> partition. The size required will + vary according to needs, here a 8GiB one is created. +<screen language="commands"># parted /dev/sda -- mkpart primary linux-swap -8GiB 100%</screen> + <note> + <para> + The swap partition size rules are no different than for other Linux + distributions. + </para> + </note> + </para> + </listitem> + <listitem> + <para> + Finally, the <emphasis>boot</emphasis> partition. NixOS by default uses + the ESP (EFI system partition) as its <emphasis>/boot</emphasis> + partition. It uses the initially reserved 512MiB at the start of the + disk. +<screen language="commands"># parted /dev/sda -- mkpart ESP fat32 1MiB 512MiB +# parted /dev/sda -- set 3 boot on</screen> + </para> + </listitem> + </orderedlist> + </para> + + <para> + Once complete, you can follow with + <xref linkend="sec-installation-partitioning-formatting"/>. + </para> + </section> + + <section xml:id="sec-installation-partitioning-MBR"> + <title>Legacy Boot (MBR)</title> + + <para> + Here's an example partition scheme for Legacy Boot, using + <filename>/dev/sda</filename> as the device. + <note> + <para> + You can safely ignore <command>parted</command>'s informational message + about needing to update /etc/fstab. + </para> + </note> + </para> + + <para> + <orderedlist> + <listitem> + <para> + Create a <emphasis>MBR</emphasis> partition table. +<screen language="commands"># parted /dev/sda -- mklabel msdos</screen> + </para> + </listitem> + <listitem> + <para> + Add the <emphasis>root</emphasis> partition. This will fill the the disk + except for the end part, where the swap will live. +<screen language="commands"># parted /dev/sda -- mkpart primary 1MiB -8GiB</screen> + </para> + </listitem> + <listitem> + <para> + Finally, add a <emphasis>swap</emphasis> partition. The size required + will vary according to needs, here a 8GiB one is created. +<screen language="commands"># parted /dev/sda -- mkpart primary linux-swap -8GiB 100%</screen> + <note> + <para> + The swap partition size rules are no different than for other Linux + distributions. + </para> + </note> + </para> + </listitem> + </orderedlist> + </para> + + <para> + Once complete, you can follow with + <xref linkend="sec-installation-partitioning-formatting"/>. + </para> + </section> + + <section xml:id="sec-installation-partitioning-formatting"> + <title>Formatting</title> + + <para> + Use the following commands: + <itemizedlist> + <listitem> + <para> For initialising Ext4 partitions: <command>mkfs.ext4</command>. It is recommended that you assign a unique symbolic label to the file system using the option <option>-L <replaceable>label</replaceable></option>, @@ -169,242 +262,249 @@ </listitem> </itemizedlist> </para> - </listitem> - <listitem> - <para> - Mount the target file system on which NixOS should be installed on - <filename>/mnt</filename>, e.g. + </section> + </section> + <section xml:id="sec-installation-installing"> + <title>Installing</title> + + <orderedlist> + <listitem> + <para> + Mount the target file system on which NixOS should be installed on + <filename>/mnt</filename>, e.g. <screen> # mount /dev/disk/by-label/nixos /mnt </screen> - </para> - </listitem> - <listitem> - <variablelist> - <varlistentry> - <term> - UEFI systems - </term> - <listitem> - <para> - Mount the boot file system on <filename>/mnt/boot</filename>, e.g. + </para> + </listitem> + <listitem> + <variablelist> + <varlistentry> + <term> + UEFI systems + </term> + <listitem> + <para> + Mount the boot file system on <filename>/mnt/boot</filename>, e.g. <screen> # mkdir -p /mnt/boot # mount /dev/disk/by-label/boot /mnt/boot </screen> - </para> - </listitem> - </varlistentry> - </variablelist> - </listitem> - <listitem> - <para> - If your machine has a limited amount of memory, you may want to activate - swap devices now (<command>swapon - <replaceable>device</replaceable></command>). The installer (or rather, the - build actions that it may spawn) may need quite a bit of RAM, depending on - your configuration. + </para> + </listitem> + </varlistentry> + </variablelist> + </listitem> + <listitem> + <para> + If your machine has a limited amount of memory, you may want to activate + swap devices now (<command>swapon + <replaceable>device</replaceable></command>). The installer (or rather, + the build actions that it may spawn) may need quite a bit of RAM, + depending on your configuration. <screen> # swapon /dev/sda2</screen> - </para> - </listitem> - <listitem> - <para> - You now need to create a file - <filename>/mnt/etc/nixos/configuration.nix</filename> that specifies the - intended configuration of the system. This is because NixOS has a - <emphasis>declarative</emphasis> configuration model: you create or edit a - description of the desired configuration of your system, and then NixOS - takes care of making it happen. The syntax of the NixOS configuration file - is described in <xref linkend="sec-configuration-syntax"/>, while a list of - available configuration options appears in - <xref + </para> + </listitem> + <listitem> + <para> + You now need to create a file + <filename>/mnt/etc/nixos/configuration.nix</filename> that specifies the + intended configuration of the system. This is because NixOS has a + <emphasis>declarative</emphasis> configuration model: you create or edit a + description of the desired configuration of your system, and then NixOS + takes care of making it happen. The syntax of the NixOS configuration file + is described in <xref linkend="sec-configuration-syntax"/>, while a list + of available configuration options appears in + <xref linkend="ch-options"/>. A minimal example is shown in - <xref + <xref linkend="ex-config"/>. - </para> - <para> - The command <command>nixos-generate-config</command> can generate an - initial configuration file for you: + </para> + <para> + The command <command>nixos-generate-config</command> can generate an + initial configuration file for you: <screen> # nixos-generate-config --root /mnt</screen> - You should then edit <filename>/mnt/etc/nixos/configuration.nix</filename> - to suit your needs: + You should then edit <filename>/mnt/etc/nixos/configuration.nix</filename> + to suit your needs: <screen> # nano /mnt/etc/nixos/configuration.nix </screen> - If you’re using the graphical ISO image, other editors may be available - (such as <command>vim</command>). If you have network access, you can also - install other editors — for instance, you can install Emacs by running - <literal>nix-env -i emacs</literal>. - </para> - <variablelist> - <varlistentry> - <term> - BIOS systems - </term> - <listitem> - <para> - You <emphasis>must</emphasis> set the option - <xref linkend="opt-boot.loader.grub.device"/> to specify on which disk - the GRUB boot loader is to be installed. Without it, NixOS cannot boot. - </para> - </listitem> - </varlistentry> - <varlistentry> - <term> - UEFI systems - </term> - <listitem> - <para> - You <emphasis>must</emphasis> set the option - <xref linkend="opt-boot.loader.systemd-boot.enable"/> to - <literal>true</literal>. <command>nixos-generate-config</command> should - do this automatically for new configurations when booted in UEFI mode. - </para> - <para> - You may want to look at the options starting with - <option><link linkend="opt-boot.loader.efi.canTouchEfiVariables">boot.loader.efi</link></option> - and - <option><link linkend="opt-boot.loader.systemd-boot.enable">boot.loader.systemd</link></option> - as well. - </para> - </listitem> - </varlistentry> - </variablelist> - <para> - If there are other operating systems running on the machine before - installing NixOS, the <xref linkend="opt-boot.loader.grub.useOSProber"/> - option can be set to <literal>true</literal> to automatically add them to - the grub menu. - </para> - <para> - Another critical option is <option>fileSystems</option>, specifying the - file systems that need to be mounted by NixOS. However, you typically - don’t need to set it yourself, because - <command>nixos-generate-config</command> sets it automatically in - <filename>/mnt/etc/nixos/hardware-configuration.nix</filename> from your - currently mounted file systems. (The configuration file - <filename>hardware-configuration.nix</filename> is included from - <filename>configuration.nix</filename> and will be overwritten by future - invocations of <command>nixos-generate-config</command>; thus, you - generally should not modify it.) - </para> - <note> + If you’re using the graphical ISO image, other editors may be available + (such as <command>vim</command>). If you have network access, you can also + install other editors — for instance, you can install Emacs by running + <literal>nix-env -i emacs</literal>. + </para> + <variablelist> + <varlistentry> + <term> + BIOS systems + </term> + <listitem> + <para> + You <emphasis>must</emphasis> set the option + <xref linkend="opt-boot.loader.grub.device"/> to specify on which disk + the GRUB boot loader is to be installed. Without it, NixOS cannot boot. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + UEFI systems + </term> + <listitem> + <para> + You <emphasis>must</emphasis> set the option + <xref linkend="opt-boot.loader.systemd-boot.enable"/> to + <literal>true</literal>. <command>nixos-generate-config</command> + should do this automatically for new configurations when booted in UEFI + mode. + </para> + <para> + You may want to look at the options starting with + <option><link linkend="opt-boot.loader.efi.canTouchEfiVariables">boot.loader.efi</link></option> + and + <option><link linkend="opt-boot.loader.systemd-boot.enable">boot.loader.systemd</link></option> + as well. + </para> + </listitem> + </varlistentry> + </variablelist> <para> - Depending on your hardware configuration or type of file system, you may - need to set the option <option>boot.initrd.kernelModules</option> to - include the kernel modules that are necessary for mounting the root file - system, otherwise the installed system will not be able to boot. (If this - happens, boot from the CD again, mount the target file system on - <filename>/mnt</filename>, fix - <filename>/mnt/etc/nixos/configuration.nix</filename> and rerun - <filename>nixos-install</filename>.) In most cases, - <command>nixos-generate-config</command> will figure out the required - modules. + If there are other operating systems running on the machine before + installing NixOS, the <xref linkend="opt-boot.loader.grub.useOSProber"/> + option can be set to <literal>true</literal> to automatically add them to + the grub menu. </para> - </note> - </listitem> - <listitem> - <para> - Do the installation: + <para> + Another critical option is <option>fileSystems</option>, specifying the + file systems that need to be mounted by NixOS. However, you typically + don’t need to set it yourself, because + <command>nixos-generate-config</command> sets it automatically in + <filename>/mnt/etc/nixos/hardware-configuration.nix</filename> from your + currently mounted file systems. (The configuration file + <filename>hardware-configuration.nix</filename> is included from + <filename>configuration.nix</filename> and will be overwritten by future + invocations of <command>nixos-generate-config</command>; thus, you + generally should not modify it.) + </para> + <note> + <para> + Depending on your hardware configuration or type of file system, you may + need to set the option <option>boot.initrd.kernelModules</option> to + include the kernel modules that are necessary for mounting the root file + system, otherwise the installed system will not be able to boot. (If this + happens, boot from the installation media again, mount the target file + system on <filename>/mnt</filename>, fix + <filename>/mnt/etc/nixos/configuration.nix</filename> and rerun + <filename>nixos-install</filename>.) In most cases, + <command>nixos-generate-config</command> will figure out the required + modules. + </para> + </note> + </listitem> + <listitem> + <para> + Do the installation: <screen> # nixos-install</screen> - Cross fingers. If this fails due to a temporary problem (such as a network - issue while downloading binaries from the NixOS binary cache), you can just - re-run <command>nixos-install</command>. Otherwise, fix your - <filename>configuration.nix</filename> and then re-run - <command>nixos-install</command>. - </para> - <para> - As the last step, <command>nixos-install</command> will ask you to set the - password for the <literal>root</literal> user, e.g. + Cross fingers. If this fails due to a temporary problem (such as a network + issue while downloading binaries from the NixOS binary cache), you can + just re-run <command>nixos-install</command>. Otherwise, fix your + <filename>configuration.nix</filename> and then re-run + <command>nixos-install</command>. + </para> + <para> + As the last step, <command>nixos-install</command> will ask you to set the + password for the <literal>root</literal> user, e.g. <screen> setting root password... Enter new UNIX password: *** -Retype new UNIX password: *** - </screen> - <note> - <para> - For unattended installations, it is possible to use - <command>nixos-install --no-root-passwd</command> in order to disable the - password prompt entirely. - </para> - </note> - </para> - </listitem> - <listitem> - <para> - If everything went well: +Retype new UNIX password: ***</screen> + <note> + <para> + For unattended installations, it is possible to use + <command>nixos-install --no-root-passwd</command> in order to disable + the password prompt entirely. + </para> + </note> + </para> + </listitem> + <listitem> + <para> + If everything went well: <screen> - # reboot</screen> - </para> - </listitem> - <listitem> - <para> - You should now be able to boot into the installed NixOS. The GRUB boot menu - shows a list of <emphasis>available configurations</emphasis> (initially - just one). Every time you change the NixOS configuration (see - <link +# reboot</screen> + </para> + </listitem> + <listitem> + <para> + You should now be able to boot into the installed NixOS. The GRUB boot + menu shows a list of <emphasis>available configurations</emphasis> + (initially just one). Every time you change the NixOS configuration (see + <link linkend="sec-changing-config">Changing Configuration</link> - ), a new item is added to the menu. This allows you to easily roll back to - a previous configuration if something goes wrong. - </para> - <para> - You should log in and change the <literal>root</literal> password with - <command>passwd</command>. - </para> - <para> - You’ll probably want to create some user accounts as well, which can be - done with <command>useradd</command>: + ), a new item is added to the menu. This allows you to easily roll back to + a previous configuration if something goes wrong. + </para> + <para> + You should log in and change the <literal>root</literal> password with + <command>passwd</command>. + </para> + <para> + You’ll probably want to create some user accounts as well, which can be + done with <command>useradd</command>: <screen> $ useradd -c 'Eelco Dolstra' -m eelco $ passwd eelco</screen> - </para> - <para> - You may also want to install some software. For instance, + </para> + <para> + You may also want to install some software. For instance, <screen> $ nix-env -qa \*</screen> - shows what packages are available, and + shows what packages are available, and <screen> $ nix-env -i w3m</screen> - install the <literal>w3m</literal> browser. - </para> - </listitem> - </orderedlist> - <para> - To summarise, <xref linkend="ex-install-sequence" /> shows a typical sequence - of commands for installing NixOS on an empty hard drive (here - <filename>/dev/sda</filename>). <xref linkend="ex-config" + install the <literal>w3m</literal> browser. + </para> + </listitem> + </orderedlist> + </section> + <section xml:id="sec-installation-summary"> + <title>Installation summary</title> + + <para> + To summarise, <xref linkend="ex-install-sequence" /> shows a typical + sequence of commands for installing NixOS on an empty hard drive (here + <filename>/dev/sda</filename>). <xref linkend="ex-config" /> shows a - corresponding configuration Nix expression. - </para> - <example xml:id='ex-install-sequence'> - <title>Commands for Installing NixOS on <filename>/dev/sda</filename></title> -<screen> -# fdisk /dev/sda # <lineannotation>(or whatever device you want to install on)</lineannotation> --- for UEFI systems only -> n # <lineannotation>(create a new partition for /boot)</lineannotation> -> 3 # <lineannotation>(make it a partition number 3)</lineannotation> -> # <lineannotation>(press enter to accept the default)</lineannotation> -> +512M # <lineannotation>(the size of the UEFI boot partition)</lineannotation> -> t # <lineannotation>(change the partition type ...)</lineannotation> -> 3 # <lineannotation>(... of the boot partition ...)</lineannotation> -> 1 # <lineannotation>(... to 'UEFI System')</lineannotation> --- for BIOS or UEFI systems -> n # <lineannotation>(create a new partition for /swap)</lineannotation> -> 2 # <lineannotation>(make it a partition number 2)</lineannotation> -> # <lineannotation>(press enter to accept the default)</lineannotation> -> +8G # <lineannotation>(the size of the swap partition)</lineannotation> -> n # <lineannotation>(create a new partition for /)</lineannotation> -> 1 # <lineannotation>(make it a partition number 1)</lineannotation> -> # <lineannotation>(press enter to accept the default)</lineannotation> -> # <lineannotation>(press enter to accept the default and use the rest of the remaining space)</lineannotation> -> a # <lineannotation>(make the partition bootable)</lineannotation> -> x # <lineannotation>(enter expert mode)</lineannotation> -> f # <lineannotation>(fix up the partition ordering)</lineannotation> -> r # <lineannotation>(exit expert mode)</lineannotation> -> w # <lineannotation>(write the partition table to disk and exit)</lineannotation> + corresponding configuration Nix expression. + </para> + + <example xml:id="ex-partition-scheme-MBR"> + <title>Example partition schemes for NixOS on <filename>/dev/sda</filename> (MBR)</title> +<screen language="commands"> +# parted /dev/sda -- mklabel msdos +# parted /dev/sda -- mkpart primary 1MiB -8GiB +# parted /dev/sda -- mkpart primary linux-swap -8GiB 100%</screen> + </example> + + <example xml:id="ex-partition-scheme-UEFI"> + <title>Example partition schemes for NixOS on <filename>/dev/sda</filename> (UEFI)</title> +<screen language="commands"> +# parted /dev/sda -- mklabel gpt +# parted /dev/sda -- mkpart primary 512MiB -8GiB +# parted /dev/sda -- mkpart primary linux-swap -8GiB 100% +# parted /dev/sda -- mkpart ESP fat32 1MiB 512MiB +# parted /dev/sda -- set 3 boot on</screen> + </example> + + <example xml:id="ex-install-sequence"> + <title>Commands for Installing NixOS on <filename>/dev/sda</filename></title> + <para> + With a partitioned disk. +<screen language="commands"> # mkfs.ext4 -L nixos /dev/sda1 # mkswap -L swap /dev/sda2 # swapon /dev/sda2 @@ -416,9 +516,11 @@ $ nix-env -i w3m</screen> # nano /mnt/etc/nixos/configuration.nix # nixos-install # reboot</screen> - </example> - <example xml:id='ex-config'> - <title>NixOS Configuration</title> + </para> + </example> + + <example xml:id='ex-config'> + <title>NixOS Configuration</title> <screen> { config, pkgs, ... }: { imports = [ @@ -438,10 +540,19 @@ $ nix-env -i w3m</screen> services.sshd.enable = true; } </screen> - </example> - <xi:include href="installing-usb.xml" /> - <xi:include href="installing-pxe.xml" /> - <xi:include href="installing-virtualbox-guest.xml" /> - <xi:include href="installing-from-other-distro.xml" /> - <xi:include href="installing-behind-a-proxy.xml" /> + </example> + </section> + <section xml:id="sec-installation-additional-notes"> + <title>Additional installation notes</title> + + <xi:include href="installing-usb.xml" /> + + <xi:include href="installing-pxe.xml" /> + + <xi:include href="installing-virtualbox-guest.xml" /> + + <xi:include href="installing-from-other-distro.xml" /> + + <xi:include href="installing-behind-a-proxy.xml" /> + </section> </chapter> diff --git a/nixos/doc/manual/release-notes/rl-1809.xml b/nixos/doc/manual/release-notes/rl-1809.xml index f0797b51340d..c5521735428b 100644 --- a/nixos/doc/manual/release-notes/rl-1809.xml +++ b/nixos/doc/manual/release-notes/rl-1809.xml @@ -3,7 +3,7 @@ xmlns:xi="http://www.w3.org/2001/XInclude" version="5.0" xml:id="sec-release-18.09"> - <title>Release 18.09 (“Jellyfish”, 2018/09/??)</title> + <title>Release 18.09 (“Jellyfish”, 2018/10/05)</title> <section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" @@ -14,7 +14,45 @@ <para> In addition to numerous new and upgraded packages, this release has the - following highlights: + following notable updates: + </para> + + <itemizedlist> + <listitem> + <para> + End of support is planned for end of April 2019, handing over to 19.03. + </para> + </listitem> + <listitem> + <para> + Platform support: x86_64-linux and x86_64-darwin as always. Support for + aarch64-linux is as with the previous releases, not equivalent to the + x86-64-linux release, but with efforts to reach parity. + </para> + </listitem> + <listitem> + <para> + Nix has been updated to 2.1; see its + <link xlink:href="https://nixos.org/nix/manual/#ssec-relnotes-2.1">release + notes</link>. + </para> + </listitem> + <listitem> + <para> + Core versions: linux: 4.14 LTS (unchanged), glibc: 2.26 → 2.27, gcc: 7 + (unchanged), systemd: 237 → 239. + </para> + </listitem> + <listitem> + <para> + Desktop version changes: gnome: 3.26 → 3.28, (KDE) plasma-desktop: 5.12 + → 5.13. + </para> + </listitem> + </itemizedlist> + + <para> + Notable changes and additions for 18.09 include: </para> <itemizedlist> @@ -70,7 +108,7 @@ $ nix-instantiate -E '(import <nixpkgsunstable> {}).gitFull' <title>New Services</title> <para> - The following new services were added since the last release: + A curated selection of new services that were added since the last release: </para> <itemizedlist> @@ -125,6 +163,303 @@ $ nix-instantiate -E '(import <nixpkgsunstable> {}).gitFull' </para> </listitem> </itemizedlist> + + <para> + Every new services: + </para> + + <itemizedlist> + <listitem> + <para> + <literal>./config/xdg/autostart.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./config/xdg/icons.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./config/xdg/menus.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./config/xdg/mime.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./hardware/brightnessctl.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./hardware/onlykey.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./hardware/video/uvcvideo/default.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./misc/documentation.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./programs/firejail.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./programs/iftop.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./programs/sedutil.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./programs/singularity.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./programs/xss-lock.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./programs/zsh/zsh-autosuggestions.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./services/admin/oxidized.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./services/backup/duplicati.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./services/backup/restic.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./services/backup/restic-rest-server.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./services/cluster/hadoop/default.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./services/databases/aerospike.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./services/databases/monetdb.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./services/desktops/bamf.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./services/desktops/flatpak.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./services/desktops/zeitgeist.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./services/development/bloop.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./services/development/jupyter/default.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./services/hardware/lcd.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./services/hardware/undervolt.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./services/misc/clipmenu.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./services/misc/gitweb.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./services/misc/serviio.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./services/misc/safeeyes.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./services/misc/sysprof.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./services/misc/weechat.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./services/monitoring/datadog-agent.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./services/monitoring/incron.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./services/networking/dnsdist.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./services/networking/freeradius.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./services/networking/hans.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./services/networking/morty.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./services/networking/ndppd.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./services/networking/ocserv.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./services/networking/owamp.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./services/networking/quagga.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./services/networking/shadowsocks.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./services/networking/stubby.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./services/networking/zeronet.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./services/security/certmgr.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./services/security/cfssl.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./services/security/oauth2_proxy_nginx.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./services/web-apps/virtlyst.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./services/web-apps/youtrack.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./services/web-servers/hitch/default.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./services/web-servers/hydron.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./services/web-servers/meguca.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./services/web-servers/nginx/gitweb.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./virtualisation/kvmgt.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>./virtualisation/qemu-guest-agent.nix</literal> + </para> + </listitem> + </itemizedlist> </section> <section xmlns="http://docbook.org/ns/docbook" diff --git a/nixos/doc/manual/release-notes/rl-1903.xml b/nixos/doc/manual/release-notes/rl-1903.xml index 1f26d4765b97..c4847b60e274 100644 --- a/nixos/doc/manual/release-notes/rl-1903.xml +++ b/nixos/doc/manual/release-notes/rl-1903.xml @@ -105,6 +105,23 @@ <varname>rabbitmq-server</varname>. </para> </listitem> + <listitem> + <para> + The <literal>light</literal> module no longer uses setuid binaries, but + udev rules. As a consequence users of that module have to belong to the + <literal>video</literal> group in order to use the executable + (i.e. <literal>users.users.yourusername.extraGroups = ["video"];</literal>). + </para> + </listitem> + <listitem> + <para> + Buildbot now supports Python 3 and its packages have been moved to + <literal>pythonPackages</literal>. The options + <option>services.buildbot-master.package</option> and + <option>services.buildbot-worker.package</option> can be used to select + the Python 2 or 3 version of the package. + </para> + </listitem> </itemizedlist> </section> diff --git a/nixos/modules/config/krb5/default.nix b/nixos/modules/config/krb5/default.nix index c22e99a0a2f1..87021a27d34f 100644 --- a/nixos/modules/config/krb5/default.nix +++ b/nixos/modules/config/krb5/default.nix @@ -79,7 +79,7 @@ in { options = { krb5 = { - enable = mkEnableOption "Whether to enable Kerberos V."; + enable = mkEnableOption "building krb5.conf, configuration file for Kerberos V"; kerberos = mkOption { type = types.package; diff --git a/nixos/modules/config/system-path.nix b/nixos/modules/config/system-path.nix index 3c24dab85e4d..c07e19bd03c4 100644 --- a/nixos/modules/config/system-path.nix +++ b/nixos/modules/config/system-path.nix @@ -140,7 +140,7 @@ in if [ -x $out/bin/glib-compile-schemas -a -w $out/share/glib-2.0/schemas ]; then $out/bin/glib-compile-schemas $out/share/glib-2.0/schemas fi - + ${config.environment.extraSetup} ''; }; diff --git a/nixos/modules/config/xdg/mime.nix b/nixos/modules/config/xdg/mime.nix index 4323a49ea1dd..cd1064630fbc 100644 --- a/nixos/modules/config/xdg/mime.nix +++ b/nixos/modules/config/xdg/mime.nix @@ -24,11 +24,11 @@ with lib; environment.extraSetup = '' if [ -w $out/share/mime ] && [ -d $out/share/mime/packages ]; then - XDG_DATA_DIRS=$out/share ${pkgs.shared-mime-info}/bin/update-mime-database -V $out/share/mime > /dev/null + XDG_DATA_DIRS=$out/share ${pkgs.buildPackages.shared-mime-info}/bin/update-mime-database -V $out/share/mime > /dev/null fi if [ -w $out/share/applications ]; then - ${pkgs.desktop-file-utils}/bin/update-desktop-database $out/share/applications + ${pkgs.buildPackages.desktop-file-utils}/bin/update-desktop-database $out/share/applications fi ''; }; diff --git a/nixos/modules/hardware/opengl.nix b/nixos/modules/hardware/opengl.nix index b371af353cf9..46d06d71333a 100644 --- a/nixos/modules/hardware/opengl.nix +++ b/nixos/modules/hardware/opengl.nix @@ -129,17 +129,17 @@ in message = "Option driSupport32Bit only makes sense on a 64-bit system."; }; - system.activationScripts.setup-opengl = - '' - 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 ${package32} /run/opengl-driver-32 - '' else '' - rm -f /run/opengl-driver-32 - ''} - ''; + systemd.tmpfiles.rules = [ + "L+ /run/opengl-driver - - - - ${package}" + ( + if pkgs.stdenv.isi686 then + "L+ /run/opengl-driver-32 - - - - opengl-driver" + else if cfg.driSupport32Bit then + "L+ /run/opengl-driver-32 - - - - ${package32}" + else + "r /run/opengl-driver-32" + ) + ]; environment.sessionVariables.LD_LIBRARY_PATH = [ "/run/opengl-driver/lib" ] ++ optional cfg.driSupport32Bit "/run/opengl-driver-32/lib"; diff --git a/nixos/modules/hardware/steam-hardware.nix b/nixos/modules/hardware/steam-hardware.nix new file mode 100644 index 000000000000..378aeffe71b5 --- /dev/null +++ b/nixos/modules/hardware/steam-hardware.nix @@ -0,0 +1,25 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.hardware.steam-hardware; + +in + +{ + options.hardware.steam-hardware = { + enable = mkOption { + type = types.bool; + default = false; + description = "Enable udev rules for Steam hardware such as the Steam Controller, other supported controllers and the HTC Vive"; + }; + }; + + config = mkIf cfg.enable { + services.udev.packages = [ + pkgs.steamPackages.steam + ]; + }; +} diff --git a/nixos/modules/hardware/video/nvidia.nix b/nixos/modules/hardware/video/nvidia.nix index eb1952280331..6944d1a4f76b 100644 --- a/nixos/modules/hardware/video/nvidia.nix +++ b/nixos/modules/hardware/video/nvidia.nix @@ -26,9 +26,73 @@ let nvidia_libs32 = (nvidiaForKernel pkgs_i686.linuxPackages).override { libsOnly = true; kernel = null; }; enabled = nvidia_x11 != null; + + cfg = config.hardware.nvidia; + optimusCfg = cfg.optimus_prime; in { + options = { + hardware.nvidia.modesetting.enable = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + Enable kernel modesetting when using the NVIDIA proprietary driver. + + Enabling this fixes screen tearing when using Optimus via PRIME (see + <option>hardware.nvidia.optimus_prime.enable</option>. This is not enabled + by default because it is not officially supported by NVIDIA and would not + work with SLI. + ''; + }; + + hardware.nvidia.optimus_prime.enable = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + Enable NVIDIA Optimus support using the NVIDIA proprietary driver via PRIME. + If enabled, the NVIDIA GPU will be always on and used for all rendering, + while enabling output to displays attached only to the integrated Intel GPU + without a multiplexer. + + Note that this option only has any effect if the "nvidia" driver is specified + in <option>services.xserver.videoDrivers</option>, and it should preferably + be the only driver there. + + If this is enabled, then the bus IDs of the NVIDIA and Intel GPUs have to be + specified (<option>hardware.nvidia.optimus_prime.nvidiaBusId</option> and + <option>hardware.nvidia.optimus_prime.intelBusId</option>). + + If you enable this, you may want to also enable kernel modesetting for the + NVIDIA driver (<option>hardware.nvidia.modesetting.enable</option>) in order + to prevent tearing. + + Note that this configuration will only be successful when a display manager + for which the <option>services.xserver.displayManager.setupCommands</option> + option is supported is used; notably, SLiM is not supported. + ''; + }; + + hardware.nvidia.optimus_prime.nvidiaBusId = lib.mkOption { + type = lib.types.string; + default = ""; + example = "PCI:1:0:0"; + description = '' + Bus ID of the NVIDIA GPU. You can find it using lspci; for example if lspci + shows the NVIDIA GPU at "01:00.0", set this option to "PCI:1:0:0". + ''; + }; + + hardware.nvidia.optimus_prime.intelBusId = lib.mkOption { + type = lib.types.string; + default = ""; + example = "PCI:0:2:0"; + description = '' + Bus ID of the Intel GPU. You can find it using lspci; for example if lspci + shows the Intel GPU at "00:02.0", set this option to "PCI:0:2:0". + ''; + }; + }; config = mkIf enabled { assertions = [ @@ -36,16 +100,62 @@ in assertion = config.services.xserver.displayManager.gdm.wayland; message = "NVidia drivers don't support wayland"; } + { + assertion = !optimusCfg.enable || + (optimusCfg.nvidiaBusId != "" && optimusCfg.intelBusId != ""); + message = '' + When NVIDIA Optimus via PRIME is enabled, the GPU bus IDs must configured. + ''; + } ]; - services.xserver.drivers = singleton - { name = "nvidia"; modules = [ nvidia_x11.bin ]; libPath = [ nvidia_x11 ]; }; + # If Optimus/PRIME is enabled, we: + # - Specify the configured NVIDIA GPU bus ID in the Device section for the + # "nvidia" driver. + # - Add the AllowEmptyInitialConfiguration option to the Screen section for the + # "nvidia" driver, in order to allow the X server to start without any outputs. + # - Add a separate Device section for the Intel GPU, using the "modesetting" + # driver and with the configured BusID. + # - Reference that Device section from the ServerLayout section as an inactive + # device. + # - Configure the display manager to run specific `xrandr` commands which will + # configure/enable displays connected to the Intel GPU. + + services.xserver.drivers = singleton { + name = "nvidia"; + modules = [ nvidia_x11.bin ]; + libPath = [ nvidia_x11 ]; + deviceSection = optionalString optimusCfg.enable + '' + BusID "${optimusCfg.nvidiaBusId}" + ''; + screenSection = + '' + Option "RandRRotation" "on" + ${optionalString optimusCfg.enable "Option \"AllowEmptyInitialConfiguration\""} + ''; + }; - services.xserver.screenSection = + services.xserver.extraConfig = optionalString optimusCfg.enable + '' + Section "Device" + Identifier "nvidia-optimus-intel" + Driver "modesetting" + BusID "${optimusCfg.intelBusId}" + Option "AccelMethod" "none" + EndSection + ''; + services.xserver.serverLayoutSection = optionalString optimusCfg.enable '' - Option "RandRRotation" "on" + Inactive "nvidia-optimus-intel" ''; + services.xserver.displayManager.setupCommands = optionalString optimusCfg.enable '' + # Added by nvidia configuration module for Optimus/PRIME. + ${pkgs.xorg.xrandr}/bin/xrandr --setprovideroutputsource modesetting NVIDIA-0 + ${pkgs.xorg.xrandr}/bin/xrandr --auto + ''; + environment.etc."nvidia/nvidia-application-profiles-rc" = mkIf nvidia_x11.useProfiles { source = "${nvidia_x11.bin}/share/nvidia/nvidia-application-profiles-rc"; }; @@ -62,6 +172,8 @@ in boot.kernelModules = [ "nvidia-uvm" ] ++ lib.optionals config.services.xserver.enable [ "nvidia" "nvidia_modeset" "nvidia_drm" ]; + # If requested enable modesetting via kernel parameter. + boot.kernelParams = optional cfg.modesetting.enable "nvidia-drm.modeset=1"; # Create /dev/nvidia-uvm when the nvidia-uvm module is loaded. services.udev.extraRules = diff --git a/nixos/modules/installer/tools/nix-fallback-paths.nix b/nixos/modules/installer/tools/nix-fallback-paths.nix index adde237c07c9..1cfc8ff8612e 100644 --- a/nixos/modules/installer/tools/nix-fallback-paths.nix +++ b/nixos/modules/installer/tools/nix-fallback-paths.nix @@ -1,6 +1,6 @@ { - x86_64-linux = "/nix/store/mxg4bbblxfns96yrz0nalxyiyjl7gj98-nix-2.1.2"; - i686-linux = "/nix/store/bgjgmbwirx63mwwychpikd7yc4k4lbjv-nix-2.1.2"; - aarch64-linux = "/nix/store/yi18azn4nwrcwvaiag04jnxc1qs38fy5-nix-2.1.2"; - x86_64-darwin = "/nix/store/fpivmcck2qpw5plrp599iraw2x9jp18k-nix-2.1.2"; + x86_64-linux = "/nix/store/cdcia67siabmj6li7vyffgv2cry86fq8-nix-2.1.3"; + i686-linux = "/nix/store/6q3xi6y5qnsv7d62b8n00hqfxi8rs2xs-nix-2.1.3"; + aarch64-linux = "/nix/store/2v93d0vimlm28jg0ms6v1i6lc0fq13pn-nix-2.1.3"; + x86_64-darwin = "/nix/store/dkjlfkrknmxbjmpfk3dg4q3nmb7m3zvk-nix-2.1.3"; } diff --git a/nixos/modules/installer/tools/nixos-generate-config.pl b/nixos/modules/installer/tools/nixos-generate-config.pl index 359caad89a72..b70faa380e54 100644 --- a/nixos/modules/installer/tools/nixos-generate-config.pl +++ b/nixos/modules/installer/tools/nixos-generate-config.pl @@ -277,8 +277,7 @@ if ($virt eq "qemu" || $virt eq "kvm" || $virt eq "bochs") { # Also for Hyper-V. if ($virt eq "microsoft") { - push @initrdAvailableKernelModules, "hv_storvsc"; - $videoDriver = "fbdev"; + push @attrs, "virtualisation.hypervGuest.enable = true;" } diff --git a/nixos/modules/installer/tools/nixos-option.sh b/nixos/modules/installer/tools/nixos-option.sh index 3f1e591b97b0..327e3e6989f7 100644 --- a/nixos/modules/installer/tools/nixos-option.sh +++ b/nixos/modules/installer/tools/nixos-option.sh @@ -82,7 +82,7 @@ evalNix(){ set -e if test $exit_code -eq 0; then - cat <<EOF + sed '/^warning: Nix search path/d' <<EOF $result EOF return 0; @@ -90,7 +90,7 @@ EOF sed -n ' /^error/ { s/, at (string):[0-9]*:[0-9]*//; p; }; /^warning: Nix search path/ { p; }; -' <<EOF +' >&2 <<EOF $result EOF exit_code=1 diff --git a/nixos/modules/misc/documentation.nix b/nixos/modules/misc/documentation.nix index 6a7105e9cdaa..09d53c322fb3 100644 --- a/nixos/modules/misc/documentation.nix +++ b/nixos/modules/misc/documentation.nix @@ -166,7 +166,7 @@ in if [ -w $out/share/info ]; then shopt -s nullglob for i in $out/share/info/*.info $out/share/info/*.info.gz; do - ${pkgs.texinfo}/bin/install-info $i $out/share/info/dir + ${pkgs.buildPackages.texinfo}/bin/install-info $i $out/share/info/dir done fi ''; diff --git a/nixos/modules/misc/version.nix b/nixos/modules/misc/version.nix index c593adcdae6e..6d78b7c593f8 100644 --- a/nixos/modules/misc/version.nix +++ b/nixos/modules/misc/version.nix @@ -5,7 +5,6 @@ with lib; let cfg = config.system.nixos; - revisionFile = "${toString pkgs.path}/.git-revision"; gitRepo = "${toString pkgs.path}/.git"; gitCommitId = lib.substring 0 7 (commitIdFromGitRepo gitRepo); in @@ -37,9 +36,7 @@ in nixos.revision = mkOption { internal = true; type = types.str; - default = if pathIsDirectory gitRepo then commitIdFromGitRepo gitRepo - else if pathExists revisionFile then fileContents revisionFile - else "master"; + default = lib.trivial.revisionWithDefault "master"; description = "The Git revision from which this NixOS configuration was built."; }; diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index f4c7cf601bf1..f55c32fa511d 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -46,6 +46,7 @@ ./hardware/opengl.nix ./hardware/pcmcia.nix ./hardware/raid/hpsa.nix + ./hardware/steam-hardware.nix ./hardware/usb-wwan.nix ./hardware/onlykey.nix ./hardware/video/amdgpu.nix @@ -283,6 +284,7 @@ ./services/hardware/tlp.nix ./services/hardware/thinkfan.nix ./services/hardware/trezord.nix + ./services/hardware/triggerhappy.nix ./services/hardware/u2f.nix ./services/hardware/udev.nix ./services/hardware/udisks2.nix @@ -689,6 +691,7 @@ ./services/web-apps/codimd.nix ./services/web-apps/frab.nix ./services/web-apps/mattermost.nix + ./services/web-apps/nextcloud.nix ./services/web-apps/nexus.nix ./services/web-apps/pgpkeyserver-lite.nix ./services/web-apps/matomo.nix @@ -732,12 +735,14 @@ ./services/x11/display-managers/lightdm.nix ./services/x11/display-managers/sddm.nix ./services/x11/display-managers/slim.nix + ./services/x11/display-managers/startx.nix ./services/x11/display-managers/xpra.nix ./services/x11/fractalart.nix ./services/x11/hardware/libinput.nix ./services/x11/hardware/multitouch.nix ./services/x11/hardware/synaptics.nix ./services/x11/hardware/wacom.nix + ./services/x11/gdk-pixbuf.nix ./services/x11/redshift.nix ./services/x11/urxvtd.nix ./services/x11/window-managers/awesome.nix diff --git a/nixos/modules/programs/bash/bash.nix b/nixos/modules/programs/bash/bash.nix index 424e1506b4c5..f664e2225550 100644 --- a/nixos/modules/programs/bash/bash.nix +++ b/nixos/modules/programs/bash/bash.nix @@ -33,7 +33,7 @@ let ''; bashAliases = concatStringsSep "\n" ( - mapAttrsFlatten (k: v: "alias ${k}='${v}'") cfg.shellAliases + mapAttrsFlatten (k: v: "alias ${k}=${escapeShellArg v}") cfg.shellAliases ); in diff --git a/nixos/modules/programs/fish.nix b/nixos/modules/programs/fish.nix index c3f742acde2e..5948fe11f92b 100644 --- a/nixos/modules/programs/fish.nix +++ b/nixos/modules/programs/fish.nix @@ -9,7 +9,7 @@ let cfg = config.programs.fish; fishAliases = concatStringsSep "\n" ( - mapAttrsFlatten (k: v: "alias ${k} '${v}'") cfg.shellAliases + mapAttrsFlatten (k: v: "alias ${k} ${escapeShellArg v}") cfg.shellAliases ); in diff --git a/nixos/modules/programs/light.nix b/nixos/modules/programs/light.nix index 6f8c389acc97..9f2a03e7e763 100644 --- a/nixos/modules/programs/light.nix +++ b/nixos/modules/programs/light.nix @@ -13,7 +13,8 @@ in default = false; type = types.bool; description = '' - Whether to install Light backlight control with setuid wrapper. + Whether to install Light backlight control command + and udev rules granting access to members of the "video" group. ''; }; }; @@ -21,6 +22,6 @@ in config = mkIf cfg.enable { environment.systemPackages = [ pkgs.light ]; - security.wrappers.light.source = "${pkgs.light.out}/bin/light"; + services.udev.packages = [ pkgs.light ]; }; } diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix index bef10b4fe614..926c6d77d3bb 100644 --- a/nixos/modules/security/pam.nix +++ b/nixos/modules/security/pam.nix @@ -548,6 +548,13 @@ in environment.etc = mapAttrsToList (n: v: makePAMService v) config.security.pam.services; + systemd.tmpfiles.rules = optionals + (any (s: s.updateWtmp) (attrValues config.security.pam.services)) + [ + "f /var/log/wtmp" + "f /var/log/lastlog" + ]; + security.pam.services = { other.text = '' diff --git a/nixos/modules/security/polkit.nix b/nixos/modules/security/polkit.nix index 04685f2c9ea1..7f1de81d5b70 100644 --- a/nixos/modules/security/polkit.nix +++ b/nixos/modules/security/polkit.nix @@ -88,11 +88,11 @@ in "polkit-agent-helper-1".source = "${pkgs.polkit.out}/lib/polkit-1/polkit-agent-helper-1"; }; - system.activationScripts.polkit = - '' - # Probably no more needed, clean up - rm -rf /var/lib/{polkit-1,PolicyKit} - ''; + systemd.tmpfiles.rules = [ + # Probably no more needed, clean up + "R /var/lib/polkit-1" + "R /var/lib/PolicyKit" + ]; users.users.polkituser = { description = "PolKit daemon"; diff --git a/nixos/modules/services/continuous-integration/buildbot/master.nix b/nixos/modules/services/continuous-integration/buildbot/master.nix index 8d767de37f00..0f07e6133bb5 100644 --- a/nixos/modules/services/continuous-integration/buildbot/master.nix +++ b/nixos/modules/services/continuous-integration/buildbot/master.nix @@ -6,8 +6,12 @@ with lib; let cfg = config.services.buildbot-master; + + python = cfg.package.pythonModule; + escapeStr = s: escape ["'"] s; - masterCfg = if cfg.masterCfg == null then pkgs.writeText "master.cfg" '' + + defaultMasterCfg = pkgs.writeText "master.cfg" '' from buildbot.plugins import * factory = util.BuildFactory() c = BuildmasterConfig = dict( @@ -27,8 +31,28 @@ let factory.addStep(step) ${cfg.extraConfig} - '' - else cfg.masterCfg; + ''; + + tacFile = pkgs.writeText "buildbot-master.tac" '' + import os + + from twisted.application import service + from buildbot.master import BuildMaster + + basedir = '${cfg.buildbotDir}' + + configfile = '${cfg.masterCfg}' + + # Default umask for server + umask = None + + # note: this line is matched against to check that this is a buildmaster + # directory; do not edit it. + application = service.Application('buildmaster') + + m = BuildMaster(basedir, configfile, umask) + m.setServiceParent(application) + ''; in { options = { @@ -66,9 +90,9 @@ in { }; masterCfg = mkOption { - type = types.nullOr types.path; + type = types.path; description = "Optionally pass master.cfg path. Other options in this configuration will be ignored."; - default = null; + default = defaultMasterCfg; example = "/etc/nixos/buildbot/master.cfg"; }; @@ -175,18 +199,25 @@ in { package = mkOption { type = types.package; - default = pkgs.buildbot-full; - defaultText = "pkgs.buildbot-full"; + default = pkgs.pythonPackages.buildbot-full; + defaultText = "pkgs.pythonPackages.buildbot-full"; description = "Package to use for buildbot."; - example = literalExample "pkgs.buildbot-full"; + example = literalExample "pkgs.python3Packages.buildbot-full"; }; packages = mkOption { - default = with pkgs; [ python27Packages.twisted git ]; + default = [ pkgs.git ]; example = literalExample "[ pkgs.git ]"; type = types.listOf types.package; description = "Packages to add to PATH for the buildbot process."; }; + + pythonPackages = mkOption { + default = pythonPackages: with pythonPackages; [ ]; + defaultText = "pythonPackages: with pythonPackages; [ ]"; + description = "Packages to add the to the PYTHONPATH of the buildbot process."; + example = literalExample "pythonPackages: with pythonPackages; [ requests ]"; + }; }; }; @@ -210,14 +241,15 @@ in { description = "Buildbot Continuous Integration Server."; after = [ "network-online.target" ]; wantedBy = [ "multi-user.target" ]; - path = cfg.packages; + path = cfg.packages ++ cfg.pythonPackages python.pkgs; + environment.PYTHONPATH = "${python.withPackages (self: cfg.pythonPackages self ++ [ cfg.package ])}/${python.sitePackages}"; preStart = '' - env > envvars - mkdir -vp ${cfg.buildbotDir} - ln -sfv ${masterCfg} ${cfg.buildbotDir}/master.cfg - rm -fv $cfg.buildbotDir}/buildbot.tac - ${cfg.package}/bin/buildbot create-master ${cfg.buildbotDir} + mkdir -vp "${cfg.buildbotDir}" + # Link the tac file so buildbot command line tools recognize the directory + ln -sf "${tacFile}" "${cfg.buildbotDir}/buildbot.tac" + ${cfg.package}/bin/buildbot create-master --db "${cfg.dbUrl}" "${cfg.buildbotDir}" + rm -f buildbot.tac.new master.cfg.sample ''; serviceConfig = { @@ -225,12 +257,11 @@ in { User = cfg.user; Group = cfg.group; WorkingDirectory = cfg.home; - ExecStart = "${cfg.package}/bin/buildbot start --nodaemon ${cfg.buildbotDir}"; + # NOTE: call twistd directly with stdout logging for systemd + ExecStart = "${python.pkgs.twisted}/bin/twistd -o --nodaemon --pidfile= --logfile - --python ${tacFile}"; }; - }; }; meta.maintainers = with lib.maintainers; [ nand0p mic92 ]; - } diff --git a/nixos/modules/services/continuous-integration/buildbot/worker.nix b/nixos/modules/services/continuous-integration/buildbot/worker.nix index 67c541570b97..4130ec918a70 100644 --- a/nixos/modules/services/continuous-integration/buildbot/worker.nix +++ b/nixos/modules/services/continuous-integration/buildbot/worker.nix @@ -7,6 +7,40 @@ with lib; let cfg = config.services.buildbot-worker; + python = cfg.package.pythonModule; + + tacFile = pkgs.writeText "aur-buildbot-worker.tac" '' + import os + from io import open + + from buildbot_worker.bot import Worker + from twisted.application import service + + basedir = '${cfg.buildbotDir}' + + # note: this line is matched against to check that this is a worker + # directory; do not edit it. + application = service.Application('buildbot-worker') + + master_url_split = '${cfg.masterUrl}'.split(':') + buildmaster_host = master_url_split[0] + port = int(master_url_split[1]) + workername = '${cfg.workerUser}' + + with open('${cfg.workerPassFile}', 'r', encoding='utf-8') as passwd_file: + passwd = passwd_file.read().strip('\r\n') + keepalive = 600 + umask = None + maxdelay = 300 + numcpus = None + allow_shutdown = None + + s = Worker(buildmaster_host, port, workername, passwd, basedir, + keepalive, umask=umask, maxdelay=maxdelay, + numcpus=numcpus, allow_shutdown=allow_shutdown) + s.setServiceParent(application) + ''; + in { options = { services.buildbot-worker = { @@ -59,6 +93,23 @@ in { description = "Specifies the Buildbot Worker password."; }; + workerPassFile = mkOption { + type = types.path; + description = "File used to store the Buildbot Worker password"; + }; + + hostMessage = mkOption { + default = null; + type = types.nullOr types.str; + description = "Description of this worker"; + }; + + adminMessage = mkOption { + default = null; + type = types.nullOr types.str; + description = "Name of the administrator of this worker"; + }; + masterUrl = mkOption { default = "localhost:9989"; type = types.str; @@ -67,23 +118,24 @@ in { package = mkOption { type = types.package; - default = pkgs.buildbot-worker; - defaultText = "pkgs.buildbot-worker"; + default = pkgs.pythonPackages.buildbot-worker; + defaultText = "pkgs.pythonPackages.buildbot-worker"; description = "Package to use for buildbot worker."; - example = literalExample "pkgs.buildbot-worker"; + example = literalExample "pkgs.python3Packages.buildbot-worker"; }; packages = mkOption { - default = with pkgs; [ python27Packages.twisted git ]; + default = with pkgs; [ git ]; example = literalExample "[ pkgs.git ]"; type = types.listOf types.package; description = "Packages to add to PATH for the buildbot process."; }; - }; }; config = mkIf cfg.enable { + services.buildbot-worker.workerPassFile = mkDefault (pkgs.writeText "buildbot-worker-password" cfg.workerPass); + users.groups = optional (cfg.group == "bbworker") { name = "bbworker"; }; @@ -104,11 +156,16 @@ in { after = [ "network.target" "buildbot-master.service" ]; wantedBy = [ "multi-user.target" ]; path = cfg.packages; + environment.PYTHONPATH = "${python.withPackages (p: [ cfg.package ])}/${python.sitePackages}"; preStart = '' - mkdir -vp ${cfg.buildbotDir} - rm -fv $cfg.buildbotDir}/buildbot.tac - ${cfg.package}/bin/buildbot-worker create-worker ${cfg.buildbotDir} ${cfg.masterUrl} ${cfg.workerUser} ${cfg.workerPass} + mkdir -vp "${cfg.buildbotDir}/info" + ${optionalString (cfg.hostMessage != null) '' + ln -sf "${pkgs.writeText "buildbot-worker-host" cfg.hostMessage}" "${cfg.buildbotDir}/info/host" + ''} + ${optionalString (cfg.adminMessage != null) '' + ln -sf "${pkgs.writeText "buildbot-worker-admin" cfg.adminMessage}" "${cfg.buildbotDir}/info/admin" + ''} ''; serviceConfig = { @@ -116,11 +173,9 @@ in { User = cfg.user; Group = cfg.group; WorkingDirectory = cfg.home; - Environment = "PYTHONPATH=${cfg.package}/lib/python2.7/site-packages:${pkgs.python27Packages.future}/lib/python2.7/site-packages"; # NOTE: call twistd directly with stdout logging for systemd - #ExecStart = "${cfg.package}/bin/buildbot-worker start --nodaemon ${cfg.buildbotDir}"; - ExecStart = "${pkgs.python27Packages.twisted}/bin/twistd -n -l - -y ${cfg.buildbotDir}/buildbot.tac"; + ExecStart = "${python.pkgs.twisted}/bin/twistd --nodaemon --pidfile= --logfile - --python ${tacFile}"; }; }; diff --git a/nixos/modules/services/desktops/profile-sync-daemon.nix b/nixos/modules/services/desktops/profile-sync-daemon.nix index e3f74df3e573..4165bb64fe46 100644 --- a/nixos/modules/services/desktops/profile-sync-daemon.nix +++ b/nixos/modules/services/desktops/profile-sync-daemon.nix @@ -4,22 +4,7 @@ with lib; let cfg = config.services.psd; - - configFile = '' - ${optionalString (cfg.users != [ ]) '' - USERS="${concatStringsSep " " cfg.users}" - ''} - - ${optionalString (cfg.browsers != [ ]) '' - BROWSERS="${concatStringsSep " " cfg.browsers}" - ''} - - ${optionalString (cfg.volatile != "") "VOLATILE=${cfg.volatile}"} - ${optionalString (cfg.daemonFile != "") "DAEMON_FILE=${cfg.daemonFile}"} - ''; - in { - options.services.psd = with types; { enable = mkOption { type = bool; @@ -28,32 +13,6 @@ in { Whether to enable the Profile Sync daemon. ''; }; - - users = mkOption { - type = listOf str; - default = [ ]; - example = [ "demo" ]; - description = '' - A list of users whose browser profiles should be sync'd to tmpfs. - ''; - }; - - browsers = mkOption { - type = listOf str; - default = [ ]; - example = [ "chromium" "firefox" ]; - description = '' - A list of browsers to sync. Available choices are: - - chromium chromium-dev conkeror.mozdev.org epiphany firefox - firefox-trunk google-chrome google-chrome-beta google-chrome-unstable - heftig-aurora icecat luakit midori opera opera-developer opera-beta - qupzilla palemoon rekonq seamonkey - - An empty list will enable all browsers. - ''; - }; - resyncTimer = mkOption { type = str; default = "1h"; @@ -66,80 +25,53 @@ in { omitted. ''; }; - - volatile = mkOption { - type = str; - default = "/run/psd-profiles"; - description = '' - The directory where browser profiles should reside(this should be - mounted as a tmpfs). Do not include a trailing backslash. - ''; - }; - - daemonFile = mkOption { - type = str; - default = "/run/psd"; - description = '' - Where the pid and backup configuration files will be stored. - ''; - }; }; config = mkIf cfg.enable { - assertions = [ - { assertion = cfg.users != []; - message = "services.psd.users must contain at least one user"; - } - ]; - systemd = { - services = { - psd = { - description = "Profile Sync daemon"; - wants = [ "psd-resync.service" "local-fs.target" ]; - wantedBy = [ "multi-user.target" ]; - preStart = "mkdir -p ${cfg.volatile}"; - - path = with pkgs; [ glibc rsync gawk ]; - - unitConfig = { - RequiresMountsFor = [ "/home/" ]; + user = { + services = { + psd = { + enable = true; + description = "Profile Sync daemon"; + wants = [ "psd-resync.service" "local-fs.target" ]; + wantedBy = [ "default.target" ]; + path = with pkgs; [ rsync kmod gawk nettools profile-sync-daemon ]; + unitConfig = { + RequiresMountsFor = [ "/home/" ]; + }; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = "yes"; + ExecStart = "${pkgs.profile-sync-daemon}/bin/profile-sync-daemon sync"; + ExecStop = "${pkgs.profile-sync-daemon}/bin/profile-sync-daemon unsync"; + }; }; - serviceConfig = { - Type = "oneshot"; - RemainAfterExit = "yes"; - ExecStart = "${pkgs.profile-sync-daemon}/bin/profile-sync-daemon sync"; - ExecStop = "${pkgs.profile-sync-daemon}/bin/profile-sync-daemon unsync"; + psd-resync = { + enable = true; + description = "Timed profile resync"; + after = [ "psd.service" ]; + wants = [ "psd-resync.timer" ]; + partOf = [ "psd.service" ]; + wantedBy = [ "default.target" ]; + path = with pkgs; [ rsync kmod gawk nettools profile-sync-daemon ]; + serviceConfig = { + Type = "oneshot"; + ExecStart = "${pkgs.profile-sync-daemon}/bin/profile-sync-daemon resync"; + }; }; }; - psd-resync = { - description = "Timed profile resync"; - after = [ "psd.service" ]; - wants = [ "psd-resync.timer" ]; - partOf = [ "psd.service" ]; - - path = with pkgs; [ glibc rsync gawk ]; + timers.psd-resync = { + description = "Timer for profile sync daemon - ${cfg.resyncTimer}"; + partOf = [ "psd-resync.service" "psd.service" ]; - serviceConfig = { - Type = "oneshot"; - ExecStart = "${pkgs.profile-sync-daemon}/bin/profile-sync-daemon resync"; + timerConfig = { + OnUnitActiveSec = "${cfg.resyncTimer}"; }; }; }; - - timers.psd-resync = { - description = "Timer for profile sync daemon - ${cfg.resyncTimer}"; - partOf = [ "psd-resync.service" "psd.service" ]; - - timerConfig = { - OnUnitActiveSec = "${cfg.resyncTimer}"; - }; - }; }; - - environment.etc."psd.conf".text = configFile; - }; } diff --git a/nixos/modules/services/hardware/triggerhappy.nix b/nixos/modules/services/hardware/triggerhappy.nix new file mode 100644 index 000000000000..81d4a1ae65bf --- /dev/null +++ b/nixos/modules/services/hardware/triggerhappy.nix @@ -0,0 +1,114 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.triggerhappy; + + socket = "/run/thd.socket"; + + configFile = pkgs.writeText "triggerhappy.conf" '' + ${concatMapStringsSep "\n" + ({ keys, event, cmd, ... }: + ''${concatMapStringsSep "+" (x: "KEY_" + x) keys} ${toString { press = 1; hold = 2; release = 0; }.${event}} ${cmd}'' + ) + cfg.bindings} + ${cfg.extraConfig} + ''; + + bindingCfg = { config, ... }: { + options = { + + keys = mkOption { + type = types.listOf types.str; + description = "List of keys to match. Key names as defined in linux/input-event-codes.h"; + }; + + event = mkOption { + type = types.enum ["press" "hold" "release"]; + default = "press"; + description = "Event to match."; + }; + + cmd = mkOption { + type = types.str; + description = "What to run."; + }; + + }; + }; + +in + +{ + + ###### interface + + options = { + + services.triggerhappy = { + + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable the <command>triggerhappy</command> hotkey daemon. + ''; + }; + + bindings = mkOption { + type = types.listOf (types.submodule bindingCfg); + default = []; + example = lib.literalExample '' + [ { keys = ["PLAYPAUSE"]; cmd = "''${pkgs.mpc_cli}/bin/mpc -q toggle"; } ] + ''; + description = '' + Key bindings for <command>triggerhappy</command>. + ''; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + description = '' + Literal contents to append to the end of <command>triggerhappy</command> configuration file. + ''; + }; + + }; + + }; + + + ###### implementation + + config = mkIf cfg.enable { + + systemd.sockets.triggerhappy = { + description = "Triggerhappy Socket"; + wantedBy = [ "sockets.target" ]; + socketConfig.ListenDatagram = socket; + }; + + systemd.services.triggerhappy = { + wantedBy = [ "multi-user.target" ]; + after = [ "local-fs.target" ]; + description = "Global hotkey daemon"; + serviceConfig = { + ExecStart = "${pkgs.triggerhappy}/bin/thd --user nobody --socket ${socket} --triggers ${configFile} --deviceglob /dev/input/event*"; + }; + }; + + services.udev.packages = lib.singleton (pkgs.writeTextFile { + name = "triggerhappy-udev-rules"; + destination = "/etc/udev/rules.d/61-triggerhappy.rules"; + text = '' + ACTION=="add", SUBSYSTEM=="input", KERNEL=="event[0-9]*", ATTRS{name}!="triggerhappy", \ + RUN+="${pkgs.triggerhappy}/bin/th-cmd --socket ${socket} --passfd --udev" + ''; + }); + + }; + +} diff --git a/nixos/modules/services/logging/journaldriver.nix b/nixos/modules/services/logging/journaldriver.nix index 74ac3d4c2365..9bd581e9ec0e 100644 --- a/nixos/modules/services/logging/journaldriver.nix +++ b/nixos/modules/services/logging/journaldriver.nix @@ -7,7 +7,7 @@ # to be set. # # For further information please consult the documentation in the -# upstream repository at: https://github.com/aprilabank/journaldriver/ +# upstream repository at: https://github.com/tazjin/journaldriver/ { config, lib, pkgs, ...}: diff --git a/nixos/modules/services/mail/rmilter.nix b/nixos/modules/services/mail/rmilter.nix index 0d91b247cd34..492c64583219 100644 --- a/nixos/modules/services/mail/rmilter.nix +++ b/nixos/modules/services/mail/rmilter.nix @@ -52,7 +52,7 @@ in enable = mkOption { type = types.bool; - default = cfg.rspamd.enable; + default = false; description = "Whether to run the rmilter daemon."; }; diff --git a/nixos/modules/services/mail/rspamd.nix b/nixos/modules/services/mail/rspamd.nix index bba11796a3d3..ff01a5dee53d 100644 --- a/nixos/modules/services/mail/rspamd.nix +++ b/nixos/modules/services/mail/rspamd.nix @@ -159,7 +159,7 @@ in services.rspamd = { - enable = mkEnableOption "Whether to run the rspamd daemon."; + enable = mkEnableOption "rspamd, the Rapid spam filtering system"; debug = mkOption { type = types.bool; diff --git a/nixos/modules/services/misc/emby.nix b/nixos/modules/services/misc/emby.nix index ff68b850cd91..0ad4a3f7376f 100644 --- a/nixos/modules/services/misc/emby.nix +++ b/nixos/modules/services/misc/emby.nix @@ -55,7 +55,7 @@ in User = cfg.user; Group = cfg.group; PermissionsStartOnly = "true"; - ExecStart = "${pkgs.emby}/bin/MediaBrowser.Server.Mono"; + ExecStart = "${pkgs.emby}/bin/emby -programdata ${cfg.dataDir}"; Restart = "on-failure"; }; }; diff --git a/nixos/modules/services/misc/gitlab.nix b/nixos/modules/services/misc/gitlab.nix index d81aa5643e53..8ea831afb7c1 100644 --- a/nixos/modules/services/misc/gitlab.nix +++ b/nixos/modules/services/misc/gitlab.nix @@ -53,6 +53,7 @@ let repos_path: "${cfg.statePath}/repositories" secret_file: "${cfg.statePath}/config/gitlab_shell_secret" log_file: "${cfg.statePath}/log/gitlab-shell.log" + custom_hooks_dir: "${cfg.statePath}/custom_hooks" redis: bin: ${pkgs.redis}/bin/redis-cli host: 127.0.0.1 @@ -562,6 +563,9 @@ in { mkdir -p ${cfg.statePath}/shell mkdir -p ${cfg.statePath}/db mkdir -p ${cfg.statePath}/uploads + mkdir -p ${cfg.statePath}/custom_hooks/pre-receive.d + mkdir -p ${cfg.statePath}/custom_hooks/post-receive.d + mkdir -p ${cfg.statePath}/custom_hooks/update.d rm -rf ${cfg.statePath}/config ${cfg.statePath}/shell/hooks mkdir -p ${cfg.statePath}/config diff --git a/nixos/modules/services/misc/nix-daemon.nix b/nixos/modules/services/misc/nix-daemon.nix index 24379ec27354..5e171c08d893 100644 --- a/nixos/modules/services/misc/nix-daemon.nix +++ b/nixos/modules/services/misc/nix-daemon.nix @@ -399,8 +399,8 @@ in systemd.sockets.nix-daemon.wantedBy = [ "sockets.target" ]; systemd.services.nix-daemon = - { path = [ nix pkgs.utillinux ] - ++ optionals cfg.distributedBuilds [ config.programs.ssh.package pkgs.gzip ] + { path = [ nix pkgs.utillinux config.programs.ssh.package ] + ++ optionals cfg.distributedBuilds [ pkgs.gzip ] ++ optionals (!isNix20) [ pkgs.openssl.bin ]; environment = cfg.envVars diff --git a/nixos/modules/services/misc/nix-optimise.nix b/nixos/modules/services/misc/nix-optimise.nix index 6f75e4dd03ea..416529f690e0 100644 --- a/nixos/modules/services/misc/nix-optimise.nix +++ b/nixos/modules/services/misc/nix-optimise.nix @@ -40,6 +40,8 @@ in systemd.services.nix-optimise = { description = "Nix Store Optimiser"; + # No point running it inside a nixos-container. It should be on the host instead. + unitConfig.ConditionVirtualization = "!container"; serviceConfig.ExecStart = "${config.nix.package}/bin/nix-store --optimise"; startAt = optionals cfg.automatic cfg.dates; }; diff --git a/nixos/modules/services/misc/redmine.nix b/nixos/modules/services/misc/redmine.nix index f763ba21d0b2..8d25ac5cb76f 100644 --- a/nixos/modules/services/misc/redmine.nix +++ b/nixos/modules/services/misc/redmine.nix @@ -5,7 +5,7 @@ with lib; let cfg = config.services.redmine; - bundle = "${pkgs.redmine}/share/redmine/bin/bundle"; + bundle = "${cfg.package}/share/redmine/bin/bundle"; databaseYml = pkgs.writeText "database.yml" '' production: @@ -15,6 +15,7 @@ let port: ${toString cfg.database.port} username: ${cfg.database.user} password: #dbpass# + ${optionalString (cfg.database.socket != null) "socket: ${cfg.database.socket}"} ''; configurationYml = pkgs.writeText "configuration.yml" '' @@ -29,6 +30,19 @@ let ${cfg.extraConfig} ''; + unpackTheme = unpack "theme"; + unpackPlugin = unpack "plugin"; + unpack = id: (name: source: + pkgs.stdenv.mkDerivation { + name = "redmine-${id}-${name}"; + buildInputs = [ pkgs.unzip ]; + buildCommand = '' + mkdir -p $out + cd $out + unpackFile ${source} + ''; + }); + in { @@ -40,6 +54,14 @@ in description = "Enable the Redmine service."; }; + package = mkOption { + type = types.package; + default = pkgs.redmine; + defaultText = "pkgs.redmine"; + description = "Which Redmine package to use."; + example = "pkgs.redmine.override { ruby = pkgs.ruby_2_3; }"; + }; + user = mkOption { type = types.str; default = "redmine"; @@ -52,6 +74,12 @@ in description = "Group under which Redmine is ran."; }; + port = mkOption { + type = types.int; + default = 3000; + description = "Port on which Redmine is ran."; + }; + stateDir = mkOption { type = types.str; default = "/var/lib/redmine"; @@ -66,6 +94,41 @@ in See https://guides.rubyonrails.org/action_mailer_basics.html#action-mailer-configuration ''; + example = literalExample '' + email_delivery: + delivery_method: smtp + smtp_settings: + address: mail.example.com + port: 25 + ''; + }; + + themes = mkOption { + type = types.attrsOf types.path; + default = {}; + description = "Set of themes."; + example = literalExample '' + { + dkuk-redmine_alex_skin = builtins.fetchurl { + url = https://bitbucket.org/dkuk/redmine_alex_skin/get/1842ef675ef3.zip; + sha256 = "0hrin9lzyi50k4w2bd2b30vrf1i4fi1c0gyas5801wn8i7kpm9yl"; + }; + } + ''; + }; + + plugins = mkOption { + type = types.attrsOf types.path; + default = {}; + description = "Set of plugins."; + example = literalExample '' + { + redmine_env_auth = builtins.fetchurl { + url = https://github.com/Intera/redmine_env_auth/archive/0.6.zip; + sha256 = "0yyr1yjd8gvvh832wdc8m3xfnhhxzk2pk3gm2psg5w9jdvd6skak"; + }; + } + ''; }; database = { @@ -78,7 +141,7 @@ in host = mkOption { type = types.str; - default = "127.0.0.1"; + default = (if cfg.database.socket != null then "localhost" else "127.0.0.1"); description = "Database host address."; }; @@ -119,6 +182,13 @@ in <option>database.user</option>. ''; }; + + socket = mkOption { + type = types.nullOr types.path; + default = null; + example = "/run/mysqld/mysqld.sock"; + description = "Path to the unix socket file to use for authentication."; + }; }; }; }; @@ -126,17 +196,20 @@ in config = mkIf cfg.enable { assertions = [ - { assertion = cfg.database.passwordFile != null || cfg.database.password != ""; - message = "either services.redmine.database.passwordFile or services.redmine.database.password must be set"; + { assertion = cfg.database.passwordFile != null || cfg.database.password != "" || cfg.database.socket != null; + message = "one of services.redmine.database.socket, services.redmine.database.passwordFile, or services.redmine.database.password must be set"; + } + { assertion = cfg.database.socket != null -> (cfg.database.type == "mysql2"); + message = "Socket authentication is only available for the mysql2 database type"; } ]; - environment.systemPackages = [ pkgs.redmine ]; + environment.systemPackages = [ cfg.package ]; systemd.services.redmine = { after = [ "network.target" (if cfg.database.type == "mysql2" then "mysql.service" else "postgresql.service") ]; wantedBy = [ "multi-user.target" ]; - environment.HOME = "${pkgs.redmine}/share/redmine"; + environment.HOME = "${cfg.package}/share/redmine"; environment.RAILS_ENV = "production"; environment.RAILS_CACHE = "${cfg.stateDir}/cache"; environment.REDMINE_LANG = "en"; @@ -151,43 +224,80 @@ in subversion ]; preStart = '' - # start with a fresh config directory every time - rm -rf ${cfg.stateDir}/config - cp -r ${pkgs.redmine}/share/redmine/config.dist ${cfg.stateDir}/config + # ensure cache directory exists for db:migrate command + mkdir -p "${cfg.stateDir}/cache" - # create the basic state directory layout pkgs.redmine expects - mkdir -p /run/redmine + # create the basic directory layout the redmine package expects + mkdir -p /run/redmine/public for i in config files log plugins tmp; do - mkdir -p ${cfg.stateDir}/$i - ln -fs ${cfg.stateDir}/$i /run/redmine/$i + mkdir -p "${cfg.stateDir}/$i" + ln -fs "${cfg.stateDir}/$i" /run/redmine/ + done + + for i in plugin_assets themes; do + mkdir -p "${cfg.stateDir}/public/$i" + ln -fs "${cfg.stateDir}/public/$i" /run/redmine/public/ done - # ensure cache directory exists for db:migrate command - mkdir -p ${cfg.stateDir}/cache + + # start with a fresh config directory + # the config directory is copied instead of linked as some mutable data is stored in there + rm -rf "${cfg.stateDir}/config/"* + cp -r ${cfg.package}/share/redmine/config.dist/* "${cfg.stateDir}/config/" # link in the application configuration - ln -fs ${configurationYml} ${cfg.stateDir}/config/configuration.yml + ln -fs ${configurationYml} "${cfg.stateDir}/config/configuration.yml" + + + # link in all user specified themes + rm -rf "${cfg.stateDir}/public/themes/"* + for theme in ${concatStringsSep " " (mapAttrsToList unpackTheme cfg.themes)}; do + ln -fs $theme/* "${cfg.stateDir}/public/themes" + done + + # link in redmine provided themes + ln -sf ${cfg.package}/share/redmine/public/themes.dist/* "${cfg.stateDir}/public/themes/" + - chmod -R ug+rwX,o-rwx+x ${cfg.stateDir}/ + # link in all user specified plugins + rm -rf "${cfg.stateDir}/plugins/"* + for plugin in ${concatStringsSep " " (mapAttrsToList unpackPlugin cfg.plugins)}; do + ln -fs $plugin/* "${cfg.stateDir}/plugins/''${plugin##*-redmine-plugin-}" + done + + + # ensure correct permissions for most files + chmod -R ug+rwX,o-rwx+x "${cfg.stateDir}/" - # handle database.passwordFile + + # handle database.passwordFile & permissions DBPASS=$(head -n1 ${cfg.database.passwordFile}) - cp -f ${databaseYml} ${cfg.stateDir}/config/database.yml - sed -e "s,#dbpass#,$DBPASS,g" -i ${cfg.stateDir}/config/database.yml - chmod 440 ${cfg.stateDir}/config/database.yml + cp -f ${databaseYml} "${cfg.stateDir}/config/database.yml" + sed -e "s,#dbpass#,$DBPASS,g" -i "${cfg.stateDir}/config/database.yml" + chmod 440 "${cfg.stateDir}/config/database.yml" + # generate a secret token if required if ! test -e "${cfg.stateDir}/config/initializers/secret_token.rb"; then ${bundle} exec rake generate_secret_token - chmod 440 ${cfg.stateDir}/config/initializers/secret_token.rb + chmod 440 "${cfg.stateDir}/config/initializers/secret_token.rb" fi + # ensure everything is owned by ${cfg.user} - chown -R ${cfg.user}:${cfg.group} ${cfg.stateDir} + chown -R ${cfg.user}:${cfg.group} "${cfg.stateDir}" + + + # execute redmine required commands prior to starting the application + # NOTE: su required in case using mysql socket authentication + /run/wrappers/bin/su -s ${pkgs.bash}/bin/bash -m -l redmine -c '${bundle} exec rake db:migrate' + /run/wrappers/bin/su -s ${pkgs.bash}/bin/bash -m -l redmine -c '${bundle} exec rake redmine:load_default_data' + - ${bundle} exec rake db:migrate - ${bundle} exec rake redmine:load_default_data + # log files don't exist until after first command has been executed + # correct ownership of files generated by calling exec rake ... + chown -R ${cfg.user}:${cfg.group} "${cfg.stateDir}/log" ''; serviceConfig = { @@ -196,13 +306,13 @@ in User = cfg.user; Group = cfg.group; TimeoutSec = "300"; - WorkingDirectory = "${pkgs.redmine}/share/redmine"; - ExecStart="${bundle} exec rails server webrick -e production -P ${cfg.stateDir}/redmine.pid"; + WorkingDirectory = "${cfg.package}/share/redmine"; + ExecStart="${bundle} exec rails server webrick -e production -p ${toString cfg.port} -P '${cfg.stateDir}/redmine.pid'"; }; }; - users.extraUsers = optionalAttrs (cfg.user == "redmine") (singleton + users.users = optionalAttrs (cfg.user == "redmine") (singleton { name = "redmine"; group = cfg.group; home = cfg.stateDir; @@ -210,7 +320,7 @@ in uid = config.ids.uids.redmine; }); - users.extraGroups = optionalAttrs (cfg.group == "redmine") (singleton + users.groups = optionalAttrs (cfg.group == "redmine") (singleton { name = "redmine"; gid = config.ids.gids.redmine; }); diff --git a/nixos/modules/services/monitoring/prometheus/alertmanager.nix b/nixos/modules/services/monitoring/prometheus/alertmanager.nix index 8a47c9f1e7d8..8a44cf7fd8f6 100644 --- a/nixos/modules/services/monitoring/prometheus/alertmanager.nix +++ b/nixos/modules/services/monitoring/prometheus/alertmanager.nix @@ -9,6 +9,15 @@ let if cfg.configText != null then pkgs.writeText "alertmanager.yml" cfg.configText else mkConfigFile; + cmdlineArgs = cfg.extraFlags ++ [ + "--config.file ${alertmanagerYml}" + "--web.listen-address ${cfg.listenAddress}:${toString cfg.port}" + "--log.level ${cfg.logLevel}" + ] ++ (optional (cfg.webExternalUrl != null) + "--web.external-url ${cfg.webExternalUrl}" + ) ++ (optional (cfg.logFormat != null) + "--log.format ${cfg.logFormat}" + ); in { options = { services.prometheus.alertmanager = { @@ -99,6 +108,14 @@ in { Open port in firewall for incoming connections. ''; }; + + extraFlags = mkOption { + type = types.listOf types.str; + default = []; + description = '' + Extra commandline options when launching the Alertmanager. + ''; + }; }; }; @@ -111,11 +128,7 @@ in { after = [ "network.target" ]; script = '' ${pkgs.prometheus-alertmanager.bin}/bin/alertmanager \ - --config.file ${alertmanagerYml} \ - --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \ - --log.level ${cfg.logLevel} \ - ${optionalString (cfg.webExternalUrl != null) ''--web.external-url ${cfg.webExternalUrl} \''} - ${optionalString (cfg.logFormat != null) "--log.format ${cfg.logFormat}"} + ${concatStringsSep " \\\n " cmdlineArgs} ''; serviceConfig = { diff --git a/nixos/modules/services/monitoring/prometheus/exporters.nix b/nixos/modules/services/monitoring/prometheus/exporters.nix index 1d5f400250fd..950af848c0f6 100644 --- a/nixos/modules/services/monitoring/prometheus/exporters.nix +++ b/nixos/modules/services/monitoring/prometheus/exporters.nix @@ -30,6 +30,7 @@ let postfix = import ./exporters/postfix.nix { inherit config lib pkgs; }; snmp = import ./exporters/snmp.nix { inherit config lib pkgs; }; surfboard = import ./exporters/surfboard.nix { inherit config lib pkgs; }; + tor = import ./exporters/tor.nix { inherit config lib pkgs; }; unifi = import ./exporters/unifi.nix { inherit config lib pkgs; }; varnish = import ./exporters/varnish.nix { inherit config lib pkgs; }; }; @@ -123,15 +124,13 @@ let systemd.services."prometheus-${name}-exporter" = mkMerge ([{ wantedBy = [ "multi-user.target" ]; after = [ "network.target" ]; - serviceConfig = { - Restart = mkDefault "always"; - PrivateTmp = mkDefault true; - WorkingDirectory = mkDefault /tmp; - } // mkIf (!(serviceOpts.serviceConfig.DynamicUser or false)) { - User = conf.user; - Group = conf.group; - }; - } serviceOpts ]); + serviceConfig.Restart = mkDefault "always"; + serviceConfig.PrivateTmp = mkDefault true; + serviceConfig.WorkingDirectory = mkDefault /tmp; + } serviceOpts ] ++ optional (serviceOpts.serviceConfig.DynamicUser or false) { + serviceConfig.User = conf.user; + serviceConfig.Group = conf.group; + }); }; in { @@ -172,5 +171,8 @@ in }) exporterOpts) ); - meta.doc = ./exporters.xml; + meta = { + doc = ./exporters.xml; + maintainers = [ maintainers.willibutz ]; + }; } diff --git a/nixos/modules/services/monitoring/prometheus/exporters/snmp.nix b/nixos/modules/services/monitoring/prometheus/exporters/snmp.nix index 404cd0a1896b..0d9194124325 100644 --- a/nixos/modules/services/monitoring/prometheus/exporters/snmp.nix +++ b/nixos/modules/services/monitoring/prometheus/exporters/snmp.nix @@ -60,10 +60,10 @@ in DynamicUser = true; ExecStart = '' ${pkgs.prometheus-snmp-exporter.bin}/bin/snmp_exporter \ - -config.file ${configFile} \ - -log.format ${cfg.logFormat} \ - -log.level ${cfg.logLevel} \ - -web.listen-address ${cfg.listenAddress}:${toString cfg.port} \ + --config.file=${configFile} \ + --log.format=${cfg.logFormat} \ + --log.level=${cfg.logLevel} \ + --web.listen-address=${cfg.listenAddress}:${toString cfg.port} \ ${concatStringsSep " \\\n " cfg.extraFlags} ''; }; diff --git a/nixos/modules/services/monitoring/prometheus/exporters/tor.nix b/nixos/modules/services/monitoring/prometheus/exporters/tor.nix new file mode 100644 index 000000000000..0e2a13c44ab7 --- /dev/null +++ b/nixos/modules/services/monitoring/prometheus/exporters/tor.nix @@ -0,0 +1,40 @@ +{ config, lib, pkgs }: + +with lib; + +let + cfg = config.services.prometheus.exporters.tor; +in +{ + port = 9130; + extraOpts = { + torControlAddress = mkOption { + type = types.str; + default = "127.0.0.1"; + description = '' + Tor control IP address or hostname. + ''; + }; + + torControlPort = mkOption { + type = types.int; + default = 9051; + description = '' + Tor control port. + ''; + }; + }; + serviceOpts = { + serviceConfig = { + DynamicUser = true; + ExecStart = '' + ${pkgs.prometheus-tor-exporter}/bin/prometheus-tor-exporter \ + -b ${cfg.listenAddress} \ + -p ${toString cfg.port} \ + -a ${cfg.torControlAddress} \ + -c ${toString cfg.torControlPort} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + }; + }; +} diff --git a/nixos/modules/services/monitoring/prometheus/exporters/varnish.nix b/nixos/modules/services/monitoring/prometheus/exporters/varnish.nix index 8dbf2d735ab9..aaed76175b84 100644 --- a/nixos/modules/services/monitoring/prometheus/exporters/varnish.nix +++ b/nixos/modules/services/monitoring/prometheus/exporters/varnish.nix @@ -69,6 +69,7 @@ in path = [ pkgs.varnish ]; serviceConfig = { DynamicUser = true; + RestartSec = mkDefault 1; ExecStart = '' ${pkgs.prometheus-varnish-exporter}/bin/prometheus_varnish_exporter \ --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \ diff --git a/nixos/modules/services/networking/bitlbee.nix b/nixos/modules/services/networking/bitlbee.nix index 392a8d5c2e7c..46e3b7457610 100644 --- a/nixos/modules/services/networking/bitlbee.nix +++ b/nixos/modules/services/networking/bitlbee.nix @@ -7,9 +7,10 @@ let cfg = config.services.bitlbee; bitlbeeUid = config.ids.uids.bitlbee; - bitlbeePkg = if cfg.libpurple_plugins == [] - then pkgs.bitlbee - else pkgs.bitlbee.override { enableLibPurple = true; }; + bitlbeePkg = pkgs.bitlbee.override { + enableLibPurple = cfg.libpurple_plugins != []; + enablePam = cfg.authBackend == "pam"; + }; bitlbeeConfig = pkgs.writeText "bitlbee.conf" '' @@ -20,6 +21,7 @@ let DaemonInterface = ${cfg.interface} DaemonPort = ${toString cfg.portNumber} AuthMode = ${cfg.authMode} + AuthBackend = ${cfg.authBackend} Plugindir = ${pkgs.bitlbee-plugins cfg.plugins}/lib/bitlbee ${lib.optionalString (cfg.hostName != "") "HostName = ${cfg.hostName}"} ${lib.optionalString (cfg.protocols != "") "Protocols = ${cfg.protocols}"} @@ -70,6 +72,16 @@ in ''; }; + authBackend = mkOption { + default = "storage"; + type = types.enum [ "storage" "pam" ]; + description = '' + How users are authenticated + storage -- save passwords internally + pam -- Linux PAM authentication + ''; + }; + authMode = mkOption { default = "Open"; type = types.enum [ "Open" "Closed" "Registered" ]; @@ -147,23 +159,22 @@ in ###### implementation - config = mkIf config.services.bitlbee.enable { - - users.users = singleton - { name = "bitlbee"; + config = mkMerge [ + (mkIf config.services.bitlbee.enable { + users.users = singleton { + name = "bitlbee"; uid = bitlbeeUid; description = "BitlBee user"; home = "/var/lib/bitlbee"; createHome = true; }; - users.groups = singleton - { name = "bitlbee"; + users.groups = singleton { + name = "bitlbee"; gid = config.ids.gids.bitlbee; }; - systemd.services.bitlbee = - { + systemd.services.bitlbee = { environment.PURPLE_PLUGIN_PATH = purple_plugin_path; description = "BitlBee IRC to other chat networks gateway"; after = [ "network.target" ]; @@ -172,8 +183,12 @@ in serviceConfig.ExecStart = "${bitlbeePkg}/sbin/bitlbee -F -n -c ${bitlbeeConfig}"; }; - environment.systemPackages = [ bitlbeePkg ]; + environment.systemPackages = [ bitlbeePkg ]; - }; + }) + (mkIf (config.services.bitlbee.authBackend == "pam") { + security.pam.services.bitlbee = {}; + }) + ]; } diff --git a/nixos/modules/services/networking/charybdis.nix b/nixos/modules/services/networking/charybdis.nix index 6d57faa9ac2b..3d02dc8d1375 100644 --- a/nixos/modules/services/networking/charybdis.nix +++ b/nixos/modules/services/networking/charybdis.nix @@ -90,7 +90,7 @@ in BANDB_DBPATH = "${cfg.statedir}/ban.db"; }; serviceConfig = { - ExecStart = "${charybdis}/bin/charybdis-ircd -foreground -logfile /dev/stdout -configfile ${configFile}"; + ExecStart = "${charybdis}/bin/charybdis -foreground -logfile /dev/stdout -configfile ${configFile}"; Group = cfg.group; User = cfg.user; PermissionsStartOnly = true; # preStart needs to run with root permissions diff --git a/nixos/modules/services/networking/miniupnpd.nix b/nixos/modules/services/networking/miniupnpd.nix index 19400edb68f9..ab714a6ac75e 100644 --- a/nixos/modules/services/networking/miniupnpd.nix +++ b/nixos/modules/services/networking/miniupnpd.nix @@ -57,32 +57,12 @@ in }; config = mkIf cfg.enable { - # from miniupnpd/netfilter/iptables_init.sh networking.firewall.extraCommands = '' - iptables -t nat -N MINIUPNPD - iptables -t nat -A PREROUTING -i ${cfg.externalInterface} -j MINIUPNPD - iptables -t mangle -N MINIUPNPD - iptables -t mangle -A PREROUTING -i ${cfg.externalInterface} -j MINIUPNPD - iptables -t filter -N MINIUPNPD - iptables -t filter -A FORWARD -i ${cfg.externalInterface} ! -o ${cfg.externalInterface} -j MINIUPNPD - iptables -t nat -N MINIUPNPD-PCP-PEER - iptables -t nat -A POSTROUTING -o ${cfg.externalInterface} -j MINIUPNPD-PCP-PEER + ${pkgs.bash}/bin/bash -x ${pkgs.miniupnpd}/etc/miniupnpd/iptables_init.sh -i ${cfg.externalInterface} ''; - # from miniupnpd/netfilter/iptables_removeall.sh networking.firewall.extraStopCommands = '' - iptables -t nat -F MINIUPNPD - iptables -t nat -D PREROUTING -i ${cfg.externalInterface} -j MINIUPNPD - iptables -t nat -X MINIUPNPD - iptables -t mangle -F MINIUPNPD - iptables -t mangle -D PREROUTING -i ${cfg.externalInterface} -j MINIUPNPD - iptables -t mangle -X MINIUPNPD - iptables -t filter -F MINIUPNPD - iptables -t filter -D FORWARD -i ${cfg.externalInterface} ! -o ${cfg.externalInterface} -j MINIUPNPD - iptables -t filter -X MINIUPNPD - iptables -t nat -F MINIUPNPD-PCP-PEER - iptables -t nat -D POSTROUTING -o ${cfg.externalInterface} -j MINIUPNPD-PCP-PEER - iptables -t nat -X MINIUPNPD-PCP-PEER + ${pkgs.bash}/bin/bash -x ${pkgs.miniupnpd}/etc/miniupnpd/iptables_removeall.sh -i ${cfg.externalInterface} ''; systemd.services.miniupnpd = { diff --git a/nixos/modules/services/networking/murmur.nix b/nixos/modules/services/networking/murmur.nix index fcc813e6898f..a6e90feff7ea 100644 --- a/nixos/modules/services/networking/murmur.nix +++ b/nixos/modules/services/networking/murmur.nix @@ -50,7 +50,7 @@ in enable = mkOption { type = types.bool; default = false; - description = "If enabled, start the Murmur Service."; + description = "If enabled, start the Murmur Mumble server."; }; autobanAttempts = mkOption { diff --git a/nixos/modules/services/networking/pptpd.nix b/nixos/modules/services/networking/pptpd.nix index 56a612b91052..d8b9e8f8341a 100644 --- a/nixos/modules/services/networking/pptpd.nix +++ b/nixos/modules/services/networking/pptpd.nix @@ -5,7 +5,7 @@ with lib; { options = { services.pptpd = { - enable = mkEnableOption "Whether pptpd should be run on startup."; + enable = mkEnableOption "pptpd, the Point-to-Point Tunneling Protocol daemon"; serverIp = mkOption { type = types.string; diff --git a/nixos/modules/services/networking/xl2tpd.nix b/nixos/modules/services/networking/xl2tpd.nix index 46111a76af80..d0a3ed7bb5e0 100644 --- a/nixos/modules/services/networking/xl2tpd.nix +++ b/nixos/modules/services/networking/xl2tpd.nix @@ -5,7 +5,7 @@ with lib; { options = { services.xl2tpd = { - enable = mkEnableOption "Whether xl2tpd should be run on startup."; + enable = mkEnableOption "xl2tpd, the Layer 2 Tunnelling Protocol Daemon"; serverIp = mkOption { type = types.string; diff --git a/nixos/modules/services/networking/xrdp.nix b/nixos/modules/services/networking/xrdp.nix index 61f22a366a02..9ed3025e47d4 100644 --- a/nixos/modules/services/networking/xrdp.nix +++ b/nixos/modules/services/networking/xrdp.nix @@ -36,7 +36,7 @@ in services.xrdp = { - enable = mkEnableOption "Whether xrdp should be run on startup."; + enable = mkEnableOption "xrdp, the Remote Desktop Protocol server"; package = mkOption { type = types.package; diff --git a/nixos/modules/services/printing/cupsd.nix b/nixos/modules/services/printing/cupsd.nix index dbf18ec1d114..1031d6f3d7e2 100644 --- a/nixos/modules/services/printing/cupsd.nix +++ b/nixos/modules/services/printing/cupsd.nix @@ -250,7 +250,7 @@ in drivers = mkOption { type = types.listOf types.path; default = []; - example = literalExample "[ pkgs.gutenprint pkgs.hplip pkgs.splix ]"; + example = literalExample "with pkgs; [ gutenprint hplip splix cups-googlecloudprint ]"; description = '' CUPS drivers to use. Drivers provided by CUPS, cups-filters, Ghostscript and Samba are added unconditionally. If this list contains diff --git a/nixos/modules/services/security/clamav.nix b/nixos/modules/services/security/clamav.nix index 9ad0095252de..04b433f8f2bf 100644 --- a/nixos/modules/services/security/clamav.nix +++ b/nixos/modules/services/security/clamav.nix @@ -95,7 +95,7 @@ in environment.etc."clamav/freshclam.conf".source = freshclamConfigFile; environment.etc."clamav/clamd.conf".source = clamdConfigFile; - systemd.services.clamav-daemon = optionalAttrs cfg.daemon.enable { + systemd.services.clamav-daemon = mkIf cfg.daemon.enable { description = "ClamAV daemon (clamd)"; after = optional cfg.updater.enable "clamav-freshclam.service"; requires = optional cfg.updater.enable "clamav-freshclam.service"; @@ -116,7 +116,7 @@ in }; }; - systemd.timers.clamav-freshclam = optionalAttrs cfg.updater.enable { + systemd.timers.clamav-freshclam = mkIf cfg.updater.enable { description = "Timer for ClamAV virus database updater (freshclam)"; wantedBy = [ "timers.target" ]; timerConfig = { @@ -125,7 +125,7 @@ in }; }; - systemd.services.clamav-freshclam = optionalAttrs cfg.updater.enable { + systemd.services.clamav-freshclam = mkIf cfg.updater.enable { description = "ClamAV virus database updater (freshclam)"; restartTriggers = [ freshclamConfigFile ]; @@ -137,6 +137,7 @@ in serviceConfig = { Type = "oneshot"; ExecStart = "${pkg}/bin/freshclam"; + SuccessExitStatus = "1"; # if databases are up to date PrivateTmp = "yes"; PrivateDevices = "yes"; }; diff --git a/nixos/modules/services/system/saslauthd.nix b/nixos/modules/services/system/saslauthd.nix index c8ddca9a0db6..8fcf4fb91fc4 100644 --- a/nixos/modules/services/system/saslauthd.nix +++ b/nixos/modules/services/system/saslauthd.nix @@ -16,7 +16,7 @@ in services.saslauthd = { - enable = mkEnableOption "Whether to enable the Cyrus SASL authentication daemon."; + enable = mkEnableOption "saslauthd, the Cyrus SASL authentication daemon"; package = mkOption { default = pkgs.cyrus_sasl.bin; diff --git a/nixos/modules/services/web-apps/nextcloud.nix b/nixos/modules/services/web-apps/nextcloud.nix new file mode 100644 index 000000000000..db4c8e1a3d85 --- /dev/null +++ b/nixos/modules/services/web-apps/nextcloud.nix @@ -0,0 +1,474 @@ +{ config, lib, pkgs, ... }@args: + +with lib; + +let + cfg = config.services.nextcloud; + + toKeyValue = generators.toKeyValue { + mkKeyValue = generators.mkKeyValueDefault {} " = "; + }; + + phpOptionsExtensions = '' + ${optionalString cfg.caching.apcu "extension=${cfg.phpPackages.apcu}/lib/php/extensions/apcu.so"} + ${optionalString cfg.caching.redis "extension=${cfg.phpPackages.redis}/lib/php/extensions/redis.so"} + ${optionalString cfg.caching.memcached "extension=${cfg.phpPackages.memcached}/lib/php/extensions/memcached.so"} + zend_extension = opcache.so + opcache.enable = 1 + ''; + phpOptions = { + upload_max_filesize = cfg.maxUploadSize; + post_max_size = cfg.maxUploadSize; + memory_limit = cfg.maxUploadSize; + } // cfg.phpOptions; + phpOptionsStr = phpOptionsExtensions + (toKeyValue phpOptions); + + occ = pkgs.writeScriptBin "nextcloud-occ" '' + #! ${pkgs.stdenv.shell} + cd ${pkgs.nextcloud} + exec /run/wrappers/bin/sudo -u nextcloud \ + NEXTCLOUD_CONFIG_DIR="${cfg.home}/config" \ + ${config.services.phpfpm.phpPackage}/bin/php \ + -c ${pkgs.writeText "php.ini" phpOptionsStr}\ + occ $* + ''; + +in { + options.services.nextcloud = { + enable = mkEnableOption "nextcloud"; + hostName = mkOption { + type = types.str; + description = "FQDN for the nextcloud instance."; + }; + home = mkOption { + type = types.str; + default = "/var/lib/nextcloud"; + description = "Storage path of nextcloud."; + }; + https = mkOption { + type = types.bool; + default = false; + description = "Enable if there is a TLS terminating proxy in front of nextcloud."; + }; + + maxUploadSize = mkOption { + default = "512M"; + type = types.str; + description = '' + Defines the upload limit for files. This changes the relevant options + in php.ini and nginx if enabled. + ''; + }; + + skeletonDirectory = mkOption { + default = ""; + type = types.str; + description = '' + The directory where the skeleton files are located. These files will be + copied to the data directory of new users. Leave empty to not copy any + skeleton files. + ''; + }; + + nginx.enable = mkEnableOption "nginx vhost management"; + + webfinger = mkOption { + type = types.bool; + default = false; + description = '' + Enable this option if you plan on using the webfinger plugin. + The appropriate nginx rewrite rules will be added to your configuration. + ''; + }; + + phpPackages = mkOption { + type = types.attrs; + default = pkgs.php71Packages; + defaultText = "pkgs.php71Packages"; + description = '' + Overridable attribute of the PHP packages set to use. If any caching + module is enabled, it will be taken from here. Therefore it should + match the version of PHP given to + <literal>services.phpfpm.phpPackage</literal>. + ''; + }; + + phpOptions = mkOption { + type = types.attrsOf types.str; + default = { + "short_open_tag" = "Off"; + "expose_php" = "Off"; + "error_reporting" = "E_ALL & ~E_DEPRECATED & ~E_STRICT"; + "display_errors" = "stderr"; + "opcache.enable_cli" = "1"; + "opcache.interned_strings_buffer" = "8"; + "opcache.max_accelerated_files" = "10000"; + "opcache.memory_consumption" = "128"; + "opcache.revalidate_freq" = "1"; + "opcache.fast_shutdown" = "1"; + "openssl.cafile" = "/etc/ssl/certs/ca-certificates.crt"; + "catch_workers_output" = "yes"; + }; + description = '' + Options for PHP's php.ini file for nextcloud. + ''; + }; + + poolConfig = mkOption { + type = types.lines; + default = '' + pm = dynamic + pm.max_children = 32 + pm.start_servers = 2 + pm.min_spare_servers = 2 + pm.max_spare_servers = 4 + pm.max_requests = 500 + ''; + description = '' + Options for nextcloud's PHP pool. See the documentation on <literal>php-fpm.conf</literal> for details on configuration directives. + ''; + }; + + config = { + dbtype = mkOption { + type = types.enum [ "sqlite" "pgsql" "mysql" ]; + default = "sqlite"; + description = "Database type."; + }; + dbname = mkOption { + type = types.nullOr types.str; + default = "nextcloud"; + description = "Database name."; + }; + dbuser = mkOption { + type = types.nullOr types.str; + default = "nextcloud"; + description = "Database user."; + }; + dbpass = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + Database password. Use <literal>dbpassFile</literal> to avoid this + being world-readable in the <literal>/nix/store</literal>. + ''; + }; + dbpassFile = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + The full path to a file that contains the database password. + ''; + }; + dbhost = mkOption { + type = types.nullOr types.str; + default = "localhost"; + description = "Database host."; + }; + dbport = mkOption { + type = with types; nullOr (either int str); + default = null; + description = "Database port."; + }; + dbtableprefix = mkOption { + type = types.nullOr types.str; + default = null; + description = "Table prefix in Nextcloud database."; + }; + adminuser = mkOption { + type = types.str; + default = "root"; + description = "Admin username."; + }; + adminpass = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + Database password. Use <literal>adminpassFile</literal> to avoid this + being world-readable in the <literal>/nix/store</literal>. + ''; + }; + adminpassFile = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + The full path to a file that contains the admin's password. + ''; + }; + + extraTrustedDomains = mkOption { + type = types.listOf types.str; + default = []; + description = '' + Trusted domains, from which the nextcloud installation will be + acessible. You don't need to add + <literal>services.nextcloud.hostname</literal> here. + ''; + }; + }; + + caching = { + apcu = mkOption { + type = types.bool; + default = true; + description = '' + Whether to load the APCu module into PHP. + ''; + }; + redis = mkOption { + type = types.bool; + default = false; + description = '' + Whether to load the Redis module into PHP. + You still need to enable Redis in your config.php. + See https://docs.nextcloud.com/server/14/admin_manual/configuration_server/caching_configuration.html + ''; + }; + memcached = mkOption { + type = types.bool; + default = false; + description = '' + Whether to load the Memcached module into PHP. + You still need to enable Memcached in your config.php. + See https://docs.nextcloud.com/server/14/admin_manual/configuration_server/caching_configuration.html + ''; + }; + }; + }; + + config = mkIf cfg.enable (mkMerge [ + { assertions = let acfg = cfg.config; in [ + { assertion = !(acfg.dbpass != null && acfg.dbpassFile != null); + message = "Please specify no more than one of dbpass or dbpassFile"; + } + { assertion = ((acfg.adminpass != null || acfg.adminpassFile != null) + && !(acfg.adminpass != null && acfg.adminpassFile != null)); + message = "Please specify exactly one of adminpass or adminpassFile"; + } + ]; + } + + { systemd.timers."nextcloud-cron" = { + wantedBy = [ "timers.target" ]; + timerConfig.OnBootSec = "5m"; + timerConfig.OnUnitActiveSec = "15m"; + timerConfig.Unit = "nextcloud-cron.service"; + }; + + systemd.services = { + "nextcloud-setup" = let + overrideConfig = pkgs.writeText "nextcloud-config.php" '' + <?php + $CONFIG = [ + 'apps_paths' => [ + [ 'path' => '${cfg.home}/apps', 'url' => '/apps', 'writable' => false ], + [ 'path' => '${cfg.home}/store-apps', 'url' => '/store-apps', 'writable' => true ], + ], + 'datadirectory' => '${cfg.home}/data', + 'skeletondirectory' => '${cfg.skeletonDirectory}', + ${optionalString cfg.caching.apcu "'memcache.local' => '\\OC\\Memcache\\APCu',"} + 'log_type' => 'syslog', + ]; + ''; + occInstallCmd = let + c = cfg.config; + adminpass = if c.adminpassFile != null + then ''"$(<"${toString c.adminpassFile}")"'' + else ''"${toString c.adminpass}"''; + dbpass = if c.dbpassFile != null + then ''"$(<"${toString c.dbpassFile}")"'' + else if c.dbpass != null + then ''"${toString c.dbpass}"'' + else null; + installFlags = concatStringsSep " \\\n " + (mapAttrsToList (k: v: "${k} ${toString v}") { + "--database" = ''"${c.dbtype}"''; + # The following attributes are optional depending on the type of + # database. Those that evaluate to null on the left hand side + # will be omitted. + ${if c.dbname != null then "--database-name" else null} = ''"${c.dbname}"''; + ${if c.dbhost != null then "--database-host" else null} = ''"${c.dbhost}"''; + ${if c.dbport != null then "--database-port" else null} = ''"${toString c.dbport}"''; + ${if c.dbuser != null then "--database-user" else null} = ''"${c.dbuser}"''; + ${if (any (x: x != null) [c.dbpass c.dbpassFile]) + then "--database-pass" else null} = dbpass; + ${if c.dbtableprefix != null + then "--database-table-prefix" else null} = ''"${toString c.dbtableprefix}"''; + "--admin-user" = ''"${c.adminuser}"''; + "--admin-pass" = adminpass; + "--data-dir" = ''"${cfg.home}/data"''; + }); + in '' + ${occ}/bin/nextcloud-occ maintenance:install \ + ${installFlags} + ''; + occSetTrustedDomainsCmd = concatStringsSep "\n" (imap0 + (i: v: '' + ${occ}/bin/nextcloud-occ config:system:set trusted_domains \ + ${toString i} --value="${toString v}" + '') ([ cfg.hostName ] ++ cfg.config.extraTrustedDomains)); + + in { + wantedBy = [ "multi-user.target" ]; + before = [ "phpfpm-nextcloud.service" ]; + script = '' + chmod og+x ${cfg.home} + ln -sf ${pkgs.nextcloud}/apps ${cfg.home}/ + mkdir -p ${cfg.home}/config ${cfg.home}/data ${cfg.home}/store-apps + ln -sf ${overrideConfig} ${cfg.home}/config/override.config.php + + chown -R nextcloud:nginx ${cfg.home}/config ${cfg.home}/data ${cfg.home}/store-apps + + # Do not install if already installed + if [[ ! -e ${cfg.home}/config/config.php ]]; then + ${occInstallCmd} + fi + + ${occ}/bin/nextcloud-occ upgrade + + ${occ}/bin/nextcloud-occ config:system:delete trusted_domains + ${occSetTrustedDomainsCmd} + ''; + serviceConfig.Type = "oneshot"; + }; + "nextcloud-cron" = { + environment.NEXTCLOUD_CONFIG_DIR = "${cfg.home}/config"; + serviceConfig.Type = "oneshot"; + serviceConfig.User = "nextcloud"; + serviceConfig.ExecStart = "${pkgs.php}/bin/php -f ${pkgs.nextcloud}/cron.php"; + }; + }; + + services.phpfpm = { + phpOptions = phpOptionsExtensions; + phpPackage = pkgs.php71; + pools.nextcloud = let + phpAdminValues = (toKeyValue + (foldr (a: b: a // b) {} + (mapAttrsToList (k: v: { "php_admin_value[${k}]" = v; }) + phpOptions))); + in { + listen = "/run/phpfpm/nextcloud"; + extraConfig = '' + listen.owner = nginx + listen.group = nginx + user = nextcloud + group = nginx + ${cfg.poolConfig} + env[NEXTCLOUD_CONFIG_DIR] = ${cfg.home}/config + env[PATH] = /run/wrappers/bin:/nix/var/nix/profiles/default/bin:/run/current-system/sw/bin:/usr/bin:/bin + ${phpAdminValues} + ''; + }; + }; + + users.extraUsers.nextcloud = { + home = "${cfg.home}"; + group = "nginx"; + createHome = true; + }; + + environment.systemPackages = [ occ ]; + } + + (mkIf cfg.nginx.enable { + services.nginx = { + enable = true; + virtualHosts = { + "${cfg.hostName}" = { + root = pkgs.nextcloud; + locations = { + "= /robots.txt" = { + priority = 100; + extraConfig = '' + allow all; + log_not_found off; + access_log off; + ''; + }; + "/" = { + priority = 200; + extraConfig = "rewrite ^ /index.php$uri;"; + }; + "~ ^/store-apps" = { + priority = 201; + extraConfig = "root ${cfg.home};"; + }; + "= /.well-known/carddav" = { + priority = 210; + extraConfig = "return 301 $scheme://$host/remote.php/dav;"; + }; + "= /.well-known/caldav" = { + priority = 210; + extraConfig = "return 301 $scheme://$host/remote.php/dav;"; + }; + "~ ^/(?:build|tests|config|lib|3rdparty|templates|data)/" = { + priority = 300; + extraConfig = "deny all;"; + }; + "~ ^/(?:\\.|autotest|occ|issue|indie|db_|console)" = { + priority = 300; + extraConfig = "deny all;"; + }; + "~ ^/(?:index|remote|public|cron|core/ajax/update|status|ocs/v[12]|updater/.+|ocs-provider/.+)\\.php(?:$|/)" = { + priority = 500; + extraConfig = '' + include ${pkgs.nginxMainline}/conf/fastcgi.conf; + fastcgi_split_path_info ^(.+\.php)(/.*)$; + fastcgi_param PATH_INFO $fastcgi_path_info; + fastcgi_param HTTPS ${if cfg.https then "on" else "off"}; + fastcgi_param modHeadersAvailable true; + fastcgi_param front_controller_active true; + fastcgi_pass unix:/run/phpfpm/nextcloud; + fastcgi_intercept_errors on; + fastcgi_request_buffering off; + fastcgi_read_timeout 120s; + ''; + }; + "~ ^/(?:updater|ocs-provider)(?:$|/)".extraConfig = '' + try_files $uri/ =404; + index index.php; + ''; + "~ \\.(?:css|js|woff|svg|gif)$".extraConfig = '' + try_files $uri /index.php$uri$is_args$args; + add_header Cache-Control "public, max-age=15778463"; + add_header X-Content-Type-Options nosniff; + add_header X-XSS-Protection "1; mode=block"; + add_header X-Robots-Tag none; + add_header X-Download-Options noopen; + add_header X-Permitted-Cross-Domain-Policies none; + access_log off; + ''; + "~ \\.(?:png|html|ttf|ico|jpg|jpeg)$".extraConfig = '' + try_files $uri /index.php$uri$is_args$args; + access_log off; + ''; + }; + extraConfig = '' + add_header X-Content-Type-Options nosniff; + add_header X-XSS-Protection "1; mode=block"; + add_header X-Robots-Tag none; + add_header X-Download-Options noopen; + add_header X-Permitted-Cross-Domain-Policies none; + error_page 403 /core/templates/403.php; + error_page 404 /core/templates/404.php; + client_max_body_size ${cfg.maxUploadSize}; + fastcgi_buffers 64 4K; + gzip on; + gzip_vary on; + gzip_comp_level 4; + gzip_min_length 256; + gzip_proxied expired no-cache no-store private no_last_modified no_etag auth; + gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy; + + ${optionalString cfg.webfinger '' + rewrite ^/.well-known/host-meta /public.php?service=host-meta last; + rewrite ^/.well-known/host-meta.json /public.php?service=host-meta-json last; + ''} + ''; + }; + }; + }; + }) + ]); +} diff --git a/nixos/modules/services/web-apps/tt-rss.nix b/nixos/modules/services/web-apps/tt-rss.nix index 2b171aa1b2b2..90b35d19ea11 100644 --- a/nixos/modules/services/web-apps/tt-rss.nix +++ b/nixos/modules/services/web-apps/tt-rss.nix @@ -624,7 +624,11 @@ let }; users = optionalAttrs (cfg.user == "tt_rss") { - users.tt_rss.group = "tt_rss"; + users.tt_rss = { + description = "tt-rss service user"; + isSystemUser = true; + group = "tt_rss"; + }; groups.tt_rss = {}; }; }; diff --git a/nixos/modules/services/web-servers/nginx/default.nix b/nixos/modules/services/web-servers/nginx/default.nix index b231ee5a3f01..508398f03ace 100644 --- a/nixos/modules/services/web-servers/nginx/default.nix +++ b/nixos/modules/services/web-servers/nginx/default.nix @@ -245,8 +245,8 @@ let } '' ) virtualHosts); - mkLocations = locations: concatStringsSep "\n" (mapAttrsToList (location: config: '' - location ${location} { + mkLocations = locations: concatStringsSep "\n" (map (config: '' + location ${config.location} { ${optionalString (config.proxyPass != null && !cfg.proxyResolveWhileRunning) "proxy_pass ${config.proxyPass};" } @@ -266,7 +266,18 @@ let ${config.extraConfig} ${optionalString (config.proxyPass != null && cfg.recommendedProxySettings) "include ${recommendedProxyConfig};"} } - '') locations); + '') (sortProperties (mapAttrsToList (k: v: v // { location = k; }) locations))); + mkBasicAuth = vhostName: authDef: let + htpasswdFile = pkgs.writeText "${vhostName}.htpasswd" ( + concatStringsSep "\n" (mapAttrsToList (user: password: '' + ${user}:{PLAIN}${password} + '') authDef) + ); + in '' + auth_basic secured; + auth_basic_user_file ${htpasswdFile}; + ''; + mkHtpasswd = vhostName: authDef: pkgs.writeText "${vhostName}.htpasswd" ( concatStringsSep "\n" (mapAttrsToList (user: password: '' ${user}:{PLAIN}${password} diff --git a/nixos/modules/services/web-servers/nginx/location-options.nix b/nixos/modules/services/web-servers/nginx/location-options.nix index 4c772734a749..9b44433d3845 100644 --- a/nixos/modules/services/web-servers/nginx/location-options.nix +++ b/nixos/modules/services/web-servers/nginx/location-options.nix @@ -71,6 +71,16 @@ with lib; These lines go to the end of the location verbatim. ''; }; + + priority = mkOption { + type = types.int; + default = 1000; + description = '' + Order of this location block in relation to the others in the vhost. + The semantics are the same as with `lib.mkOrder`. Smaller values have + a greater priority. + ''; + }; }; } diff --git a/nixos/modules/services/x11/desktop-managers/gnome3.nix b/nixos/modules/services/x11/desktop-managers/gnome3.nix index eb86f7b53bb6..0d5b860d4617 100644 --- a/nixos/modules/services/x11/desktop-managers/gnome3.nix +++ b/nixos/modules/services/x11/desktop-managers/gnome3.nix @@ -133,7 +133,6 @@ in { fonts.fonts = [ pkgs.dejavu_fonts pkgs.cantarell-fonts ]; - services.xserver.displayManager.gdm.enable = mkDefault true; services.xserver.displayManager.extraSessionFilePackages = [ pkgs.gnome3.gnome-session ]; services.xserver.displayManager.sessionCommands = '' diff --git a/nixos/modules/services/x11/desktop-managers/plasma5.nix b/nixos/modules/services/x11/desktop-managers/plasma5.nix index e759f69db897..704cc78c1528 100644 --- a/nixos/modules/services/x11/desktop-managers/plasma5.nix +++ b/nixos/modules/services/x11/desktop-managers/plasma5.nix @@ -64,7 +64,7 @@ in }; security.wrappers = { - kcheckpass.source = "${lib.getBin plasma5.plasma-workspace}/lib/libexec/kcheckpass"; + kcheckpass.source = "${lib.getBin plasma5.kscreenlocker}/lib/libexec/kcheckpass"; "start_kdeinit".source = "${lib.getBin pkgs.kinit}/lib/libexec/kf5/start_kdeinit"; kwin_wayland = { source = "${lib.getBin plasma5.kwin}/bin/kwin_wayland"; @@ -185,10 +185,8 @@ in target = "X11/xkb"; }; - environment.variables = { - # Enable GTK applications to load SVG icons - GDK_PIXBUF_MODULE_FILE = "${pkgs.librsvg.out}/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache"; - }; + # Enable GTK applications to load SVG icons + services.xserver.gdk-pixbuf.modulePackages = [ pkgs.librsvg ]; fonts.fonts = with pkgs; [ noto-fonts hack-font ]; fonts.fontconfig.defaultFonts = { @@ -225,11 +223,8 @@ in security.pam.services.sddm.enableKwallet = true; security.pam.services.slim.enableKwallet = true; - # Update the start menu for each user that has `isNormalUser` set. - system.activationScripts.plasmaSetup = stringAfter [ "users" "groups" ] - (concatStringsSep "\n" - (mapAttrsToList (name: value: "${pkgs.su}/bin/su ${name} -c ${pkgs.libsForQt5.kservice}/bin/kbuildsycoca5") - (filterAttrs (n: v: v.isNormalUser) config.users.users))); + # Update the start menu for each user that is currently logged in + system.userActivationScripts.plasmaSetup = "${pkgs.libsForQt5.kservice}/bin/kbuildsycoca5"; }) ]; diff --git a/nixos/modules/services/x11/desktop-managers/xfce.nix b/nixos/modules/services/x11/desktop-managers/xfce.nix index 75b9a76e1924..dabf09418da7 100644 --- a/nixos/modules/services/x11/desktop-managers/xfce.nix +++ b/nixos/modules/services/x11/desktop-managers/xfce.nix @@ -101,10 +101,11 @@ in ]; environment.variables = { - GDK_PIXBUF_MODULE_FILE = "${pkgs.librsvg.out}/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache"; GIO_EXTRA_MODULES = [ "${pkgs.xfce.gvfs}/lib/gio/modules" ]; }; + services.xserver.gdk-pixbuf.modulePackages = [ pkgs.librsvg ]; + services.xserver.desktopManager.session = [{ name = "xfce"; bgSupport = true; diff --git a/nixos/modules/services/x11/display-managers/default.nix b/nixos/modules/services/x11/display-managers/default.nix index 357fa8ce8f36..26b79730dd38 100644 --- a/nixos/modules/services/x11/display-managers/default.nix +++ b/nixos/modules/services/x11/display-managers/default.nix @@ -222,6 +222,17 @@ in description = "List of arguments for the X server."; }; + setupCommands = mkOption { + type = types.lines; + default = ""; + description = '' + Shell commands executed just after the X server has started. + + This option is only effective for display managers for which this feature + is supported; currently these are LightDM, GDM and SDDM. + ''; + }; + sessionCommands = mkOption { type = types.lines; default = ""; diff --git a/nixos/modules/services/x11/display-managers/gdm.nix b/nixos/modules/services/x11/display-managers/gdm.nix index a775dd0f0e04..6cc30b218f4a 100644 --- a/nixos/modules/services/x11/display-managers/gdm.nix +++ b/nixos/modules/services/x11/display-managers/gdm.nix @@ -7,6 +7,13 @@ let cfg = config.services.xserver.displayManager; gdm = pkgs.gnome3.gdm; + xSessionWrapper = if (cfg.setupCommands == "") then null else + pkgs.writeScript "gdm-x-session-wrapper" '' + #!${pkgs.bash}/bin/bash + ${cfg.setupCommands} + exec "$@" + ''; + in { @@ -112,6 +119,11 @@ in GDM_SESSIONS_DIR = "${cfg.session.desktops}/share/xsessions"; # Find the mouse XCURSOR_PATH = "~/.icons:${pkgs.gnome3.adwaita-icon-theme}/share/icons"; + } // optionalAttrs (xSessionWrapper != null) { + # Make GDM use this wrapper before running the session, which runs the + # configured setupCommands. This relies on a patched GDM which supports + # this environment variable. + GDM_X_SESSION_WRAPPER = "${xSessionWrapper}"; }; execCmd = "exec ${gdm}/bin/gdm"; }; @@ -142,7 +154,10 @@ in systemd.user.services.dbus.wantedBy = [ "default.target" ]; - programs.dconf.profiles.gdm = "${gdm}/share/dconf/profile/gdm"; + programs.dconf.profiles.gdm = pkgs.writeText "dconf-gdm-profile" '' + system-db:local + ${gdm}/share/dconf/profile/gdm + ''; # Use AutomaticLogin if delay is zero, because it's immediate. # Otherwise with TimedLogin with zero seconds the prompt is still diff --git a/nixos/modules/services/x11/display-managers/lightdm-greeters/enso-os.nix b/nixos/modules/services/x11/display-managers/lightdm-greeters/enso-os.nix new file mode 100644 index 000000000000..7c794b1ba175 --- /dev/null +++ b/nixos/modules/services/x11/display-managers/lightdm-greeters/enso-os.nix @@ -0,0 +1,159 @@ +{ config, lib, pkgs, ... }: + +with lib; +let + dmcfg = config.services.xserver.displayManager; + ldmcfg = dmcfg.lightdm; + cfg = ldmcfg.greeters.enso; + + theme = cfg.theme.package; + icons = cfg.iconTheme.package; + cursors = cfg.cursorTheme.package; + + # We need a few things in the environment for the greeter to run with + # fonts/icons. + wrappedEnsoGreeter = pkgs.runCommand "lightdm-enso-os-greeter" + { buildInputs = [ pkgs.makeWrapper ]; } + '' + # This wrapper ensures that we actually get themes + makeWrapper ${pkgs.lightdm-enso-os-greeter}/bin/pantheon-greeter \ + $out/greeter \ + --prefix PATH : "${pkgs.glibc.bin}/bin" \ + --set GDK_PIXBUF_MODULE_FILE "${pkgs.librsvg.out}/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache" \ + --set GTK_PATH "${theme}:${pkgs.gtk3.out}" \ + --set GTK_EXE_PREFIX "${theme}" \ + --set GTK_DATA_PREFIX "${theme}" \ + --set XDG_DATA_DIRS "${theme}/share:${icons}/share:${cursors}/share" \ + --set XDG_CONFIG_HOME "${theme}/share" + + cat - > $out/lightdm-enso-os-greeter.desktop << EOF + [Desktop Entry] + Name=LightDM Greeter + Comment=This runs the LightDM Greeter + Exec=$out/greeter + Type=Application + EOF + ''; + + ensoGreeterConf = pkgs.writeText "lightdm-enso-os-greeter.conf" '' + [greeter] + default-wallpaper=${ldmcfg.background} + gtk-theme=${cfg.theme.name} + icon-theme=${cfg.iconTheme.name} + cursor-theme=${cfg.cursorTheme.name} + blur=${toString cfg.blur} + brightness=${toString cfg.brightness} + ${cfg.extraConfig} + ''; +in { + options = { + services.xserver.displayManager.lightdm.greeters.enso = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable enso-os-greeter as the lightdm greeter + ''; + }; + + theme = { + package = mkOption { + type = types.package; + default = pkgs.gnome3.gnome-themes-extra; + defaultText = "pkgs.gnome3.gnome-themes-extra"; + description = '' + The package path that contains the theme given in the name option. + ''; + }; + + name = mkOption { + type = types.str; + default = "Adwaita"; + description = '' + Name of the theme to use for the lightdm-enso-os-greeter + ''; + }; + }; + + iconTheme = { + package = mkOption { + type = types.package; + default = pkgs.papirus-icon-theme; + defaultText = "pkgs.papirus-icon-theme"; + description = '' + The package path that contains the icon theme given in the name option. + ''; + }; + + name = mkOption { + type = types.str; + default = "ePapirus"; + description = '' + Name of the icon theme to use for the lightdm-enso-os-greeter + ''; + }; + }; + + cursorTheme = { + package = mkOption { + type = types.package; + default = pkgs.capitaine-cursors; + defaultText = "pkgs.capitaine-cursors"; + description = '' + The package path that contains the cursor theme given in the name option. + ''; + }; + + name = mkOption { + type = types.str; + default = "capitane-cursors"; + description = '' + Name of the cursor theme to use for the lightdm-enso-os-greeter + ''; + }; + }; + + blur = mkOption { + type = types.bool; + default = false; + description = '' + Whether or not to enable blur + ''; + }; + + brightness = mkOption { + type = types.int; + default = 7; + description = '' + Brightness + ''; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + description = '' + Extra configuration that should be put in the greeter.conf + configuration file + ''; + }; + }; + }; + + config = mkIf (ldmcfg.enable && cfg.enable) { + environment.etc."lightdm/greeter.conf".source = ensoGreeterConf; + + services.xserver.displayManager.lightdm = { + greeter = mkDefault { + package = wrappedEnsoGreeter; + name = "lightdm-enso-os-greeter"; + }; + + greeters = { + gtk = { + enable = mkDefault false; + }; + }; + }; + }; +} diff --git a/nixos/modules/services/x11/display-managers/lightdm.nix b/nixos/modules/services/x11/display-managers/lightdm.nix index 8078b93a7574..a685dbfff2a0 100644 --- a/nixos/modules/services/x11/display-managers/lightdm.nix +++ b/nixos/modules/services/x11/display-managers/lightdm.nix @@ -46,6 +46,7 @@ let greeters-directory = ${cfg.greeter.package} ''} sessions-directory = ${dmcfg.session.desktops}/share/xsessions + ${cfg.extraConfig} [Seat:*] xserver-command = ${xserverWrapper} @@ -61,6 +62,12 @@ let ${optionalString hasDefaultUserSession '' user-session=${defaultSessionName} ''} + ${optionalString (dmcfg.setupCommands != "") '' + display-setup-script=${pkgs.writeScript "lightdm-display-setup" '' + #!${pkgs.bash}/bin/bash + ${dmcfg.setupCommands} + ''} + ''} ${cfg.extraSeatDefaults} ''; @@ -73,6 +80,7 @@ in imports = [ ./lightdm-greeters/gtk.nix ./lightdm-greeters/mini.nix + ./lightdm-greeters/enso-os.nix ]; options = { @@ -113,6 +121,15 @@ in }; }; + extraConfig = mkOption { + type = types.lines; + default = ""; + example = '' + user-authority-in-system-dir = true + ''; + description = "Extra lines to append to LightDM section."; + }; + background = mkOption { type = types.str; default = "${pkgs.nixos-artwork.wallpapers.simple-dark-gray-bottom}/share/artwork/gnome/nix-wallpaper-simple-dark-gray_bottom.png"; diff --git a/nixos/modules/services/x11/display-managers/sddm.nix b/nixos/modules/services/x11/display-managers/sddm.nix index 2a9826177737..522a0dc92d6f 100644 --- a/nixos/modules/services/x11/display-managers/sddm.nix +++ b/nixos/modules/services/x11/display-managers/sddm.nix @@ -20,6 +20,7 @@ let Xsetup = pkgs.writeScript "Xsetup" '' #!/bin/sh ${cfg.setupScript} + ${dmcfg.setupCommands} ''; Xstop = pkgs.writeScript "Xstop" '' @@ -137,7 +138,8 @@ in xrandr --auto ''; description = '' - A script to execute when starting the display server. + A script to execute when starting the display server. DEPRECATED, please + use <option>services.xserver.displayManager.setupCommands</option>. ''; }; diff --git a/nixos/modules/services/x11/display-managers/startx.nix b/nixos/modules/services/x11/display-managers/startx.nix new file mode 100644 index 000000000000..15609540a6e7 --- /dev/null +++ b/nixos/modules/services/x11/display-managers/startx.nix @@ -0,0 +1,44 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.xserver.displayManager.startx; + +in + +{ + + ###### interface + + options = { + services.xserver.displayManager.startx = { + enable = mkOption { + default = false; + description = '' + Whether to enable the dummy "startx" pseudo-display manager, + which allows users to start X manually via the "startx" command + from a vt shell. The X server runs under the user's id, not as root. + The user must provide a ~/.xinintrc file containing session startup + commands, see startx(1). This is not autmatically generated + from the desktopManager and windowManager settings. + ''; + }; + }; + }; + + + ###### implementation + + config = mkIf cfg.enable { + services.xserver = { + exportConfiguration = true; + displayManager.job.execCmd = ""; + displayManager.lightdm.enable = lib.mkForce false; + }; + systemd.services.display-manager.enable = false; + environment.systemPackages = with pkgs; [ xorg.xinit ]; + }; + +} diff --git a/nixos/modules/services/x11/gdk-pixbuf.nix b/nixos/modules/services/x11/gdk-pixbuf.nix new file mode 100644 index 000000000000..58faa8e2f9df --- /dev/null +++ b/nixos/modules/services/x11/gdk-pixbuf.nix @@ -0,0 +1,45 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.xserver.gdk-pixbuf; + + # Get packages to generate the cache for. We always include gdk_pixbuf. + effectivePackages = unique ([pkgs.gdk_pixbuf] ++ cfg.modulePackages); + + # Generate the cache file by running gdk-pixbuf-query-loaders for each + # package and concatenating the results. + loadersCache = pkgs.runCommand "gdk-pixbuf-loaders.cache" {} '' + ( + for package in ${concatStringsSep " " effectivePackages}; do + module_dir="$package/${pkgs.gdk_pixbuf.moduleDir}" + if [[ ! -d $module_dir ]]; then + echo "Warning (services.xserver.gdk-pixbuf): missing module directory $module_dir" 1>&2 + continue + fi + GDK_PIXBUF_MODULEDIR="$module_dir" \ + ${pkgs.gdk_pixbuf.dev}/bin/gdk-pixbuf-query-loaders + done + ) > "$out" + ''; +in + +{ + options = { + services.xserver.gdk-pixbuf.modulePackages = mkOption { + type = types.listOf types.package; + default = [ ]; + description = "Packages providing GDK-Pixbuf modules, for cache generation."; + }; + }; + + # If there is any package configured in modulePackages, we generate the + # loaders.cache based on that and set the environment variable + # GDK_PIXBUF_MODULE_FILE to point to it. + config = mkIf (cfg.modulePackages != []) { + environment.variables = { + GDK_PIXBUF_MODULE_FILE = "${loadersCache}"; + }; + }; +} diff --git a/nixos/modules/services/x11/xserver.nix b/nixos/modules/services/x11/xserver.nix index 75bfeaac1fa3..297e36311656 100644 --- a/nixos/modules/services/x11/xserver.nix +++ b/nixos/modules/services/x11/xserver.nix @@ -374,6 +374,12 @@ in description = "Contents of the first Monitor section of the X server configuration file."; }; + extraConfig = mkOption { + type = types.lines; + default = ""; + description = "Additional contents (sections) included in the X server configuration file"; + }; + xrandrHeads = mkOption { default = []; example = [ @@ -754,6 +760,7 @@ in Driver "${driver.driverName or driver.name}" ${if cfg.useGlamor then ''Option "AccelMethod" "glamor"'' else ""} ${cfg.deviceSection} + ${driver.deviceSection or ""} ${xrandrDeviceSection} EndSection @@ -765,6 +772,7 @@ in ''} ${cfg.screenSection} + ${driver.screenSection or ""} ${optionalString (cfg.defaultDepth != 0) '' DefaultDepth ${toString cfg.defaultDepth} @@ -794,6 +802,8 @@ in '')} ${xrandrMonitorSections} + + ${cfg.extraConfig} ''; fonts.enableDefaultFonts = mkDefault true; diff --git a/nixos/modules/system/activation/activation-script.nix b/nixos/modules/system/activation/activation-script.nix index 93a1b13a81dd..b1eaf0189562 100644 --- a/nixos/modules/system/activation/activation-script.nix +++ b/nixos/modules/system/activation/activation-script.nix @@ -100,6 +100,52 @@ in exit $_status ''; }; + }; + + system.userActivationScripts = mkOption { + default = {}; + + example = literalExample '' + { plasmaSetup = { + text = ''' + ${pkgs.libsForQt5.kservice}/bin/kbuildsycoca5" + '''; + deps = []; + }; + } + ''; + + description = '' + A set of shell script fragments that are executed by a systemd user + service when a NixOS system configuration is activated. Examples are + rebuilding the .desktop file cache for showing applications in the menu. + Since these are executed every time you run + <command>nixos-rebuild</command>, it's important that they are + idempotent and fast. + ''; + + type = types.attrsOf types.unspecified; + + apply = set: { + script = '' + unset PATH + for i in ${toString path}; do + PATH=$PATH:$i/bin:$i/sbin + done + + _status=0 + trap "_status=1 _localstatus=\$?" ERR + + ${ + let + set' = mapAttrs (n: v: if isString v then noDepEntry v else v) set; + withHeadlines = addAttributeName set'; + in textClosureMap id (withHeadlines) (attrNames withHeadlines) + } + + exit $_status + ''; + }; }; @@ -128,14 +174,6 @@ in '' # Various log/runtime directories. - mkdir -m 0755 -p /run/nix/current-load # for distributed builds - mkdir -m 0700 -p /run/nix/remote-stores - - mkdir -m 0755 -p /var/log - - touch /var/log/wtmp /var/log/lastlog # must exist - chmod 644 /var/log/wtmp /var/log/lastlog - mkdir -m 1777 -p /var/tmp # Empty, immutable home directory of many system accounts. @@ -177,6 +215,14 @@ in source ${config.system.build.earlyMountScript} ''; + systemd.user = { + services.nixos-activation = { + description = "Run user specific NixOS activation"; + script = config.system.userActivationScripts.script; + unitConfig.ConditionUser = "!@system"; + serviceConfig.Type = "oneshot"; + }; + }; }; } diff --git a/nixos/modules/system/activation/switch-to-configuration.pl b/nixos/modules/system/activation/switch-to-configuration.pl index c3e469e4b8a1..397b308b7311 100644 --- a/nixos/modules/system/activation/switch-to-configuration.pl +++ b/nixos/modules/system/activation/switch-to-configuration.pl @@ -419,7 +419,8 @@ while (my $f = <$listActiveUsers>) { my ($uid, $name) = ($+{uid}, $+{user}); print STDERR "reloading user units for $name...\n"; - system("su", "-s", "@shell@", "-l", $name, "-c", "XDG_RUNTIME_DIR=/run/user/$uid @systemd@/bin/systemctl --user daemon-reload"); + system("@su@", "-s", "@shell@", "-l", $name, "-c", "XDG_RUNTIME_DIR=/run/user/$uid @systemd@/bin/systemctl --user daemon-reload"); + system("@su@", "-s", "@shell@", "-l", $name, "-c", "XDG_RUNTIME_DIR=/run/user/$uid @systemd@/bin/systemctl --user start nixos-activation.service"); } close $listActiveUsers; diff --git a/nixos/modules/system/activation/top-level.nix b/nixos/modules/system/activation/top-level.nix index 413543df88c6..a560af5ce96d 100644 --- a/nixos/modules/system/activation/top-level.nix +++ b/nixos/modules/system/activation/top-level.nix @@ -109,6 +109,7 @@ let inherit (pkgs) utillinux coreutils; systemd = config.systemd.package; shell = "${pkgs.bash}/bin/sh"; + su = "${pkgs.shadow.su}/bin/su"; inherit children; kernelParams = config.boot.kernelParams; diff --git a/nixos/modules/system/boot/initrd-network.nix b/nixos/modules/system/boot/initrd-network.nix index 384ae909b701..dd0ea69e9685 100644 --- a/nixos/modules/system/boot/initrd-network.nix +++ b/nixos/modules/system/boot/initrd-network.nix @@ -6,11 +6,22 @@ let cfg = config.boot.initrd.network; + dhcpinterfaces = lib.attrNames (lib.filterAttrs (iface: v: v.useDHCP == true) (config.networking.interfaces or {})); + udhcpcScript = pkgs.writeScript "udhcp-script" '' #! /bin/sh if [ "$1" = bound ]; then ip address add "$ip/$mask" dev "$interface" + if [ -n "$mtu" ]; then + ip link set mtu "$mtu" dev "$interface" + fi + if [ -n "$staticroutes" ]; then + echo "$staticroutes" \ + | sed -r "s@(\S+) (\S+)@ ip route add \"\1\" via \"\2\" dev \"$interface\" ; @g" \ + | sed -r "s@ via \"0\.0\.0\.0\"@@g" \ + | /bin/sh + fi if [ -n "$router" ]; then ip route add "$router" dev "$interface" # just in case if "$router" is not within "$ip/$mask" (e.g. Hetzner Cloud) ip route add default via "$router" dev "$interface" @@ -93,18 +104,24 @@ in '' # Otherwise, use DHCP. - + optionalString config.networking.useDHCP '' + + optionalString (config.networking.useDHCP || dhcpinterfaces != []) '' if [ -z "$hasNetwork" ]; then # Bring up all interfaces. - for iface in $(cd /sys/class/net && ls); do + for iface in $(ls /sys/class/net/); 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} ${udhcpcArgs} && hasNetwork=1 + # Acquire DHCP leases. + for iface in ${ if config.networking.useDHCP then + "$(ls /sys/class/net/ | grep -v ^lo$)" + else + lib.concatMapStringsSep " " lib.escapeShellArg dhcpinterfaces + }; do + echo "acquiring IP address via DHCP on $iface..." + udhcpc --quit --now -i $iface -O staticroutes --script ${udhcpcScript} ${udhcpcArgs} && hasNetwork=1 + done fi '' diff --git a/nixos/modules/system/boot/luksroot.nix b/nixos/modules/system/boot/luksroot.nix index 1079089bc5a8..018e7b2e7f89 100644 --- a/nixos/modules/system/boot/luksroot.nix +++ b/nixos/modules/system/boot/luksroot.nix @@ -7,19 +7,19 @@ let commonFunctions = '' die() { - echo "$@" >&2 - exit 1 + echo "$@" >&2 + exit 1 } dev_exist() { - local target="$1" - if [ -e $target ]; then - return 0 - else - local uuid=$(echo -n $target | sed -e 's,UUID=\(.*\),\1,g') - local dev=$(blkid --uuid $uuid) - return $? - fi + local target="$1" + if [ -e $target ]; then + return 0 + else + local uuid=$(echo -n $target | sed -e 's,UUID=\(.*\),\1,g') + blkid --uuid $uuid >/dev/null + return $? + fi } wait_target() { @@ -51,30 +51,30 @@ let } wait_yubikey() { - local secs="''${1:-10}" - - ykinfo -v 1>/dev/null 2>&1 - if [ $? != 0 ]; then - echo -n "Waiting $secs seconds for Yubikey to appear..." - local success=false - for try in $(seq $secs); do - echo -n . - sleep 1 - ykinfo -v 1>/dev/null 2>&1 - if [ $? == 0 ]; then - success=true - break - fi - done - if [ $success == true ]; then - echo " - success"; - return 0 - else - echo " - failure"; - return 1 - fi - fi - return 0 + local secs="''${1:-10}" + + ykinfo -v 1>/dev/null 2>&1 + if [ $? != 0 ]; then + echo -n "Waiting $secs seconds for Yubikey to appear..." + local success=false + for try in $(seq $secs); do + echo -n . + sleep 1 + ykinfo -v 1>/dev/null 2>&1 + if [ $? == 0 ]; then + success=true + break + fi + done + if [ $success == true ]; then + echo " - success"; + return 0 + else + echo " - failure"; + return 1 + fi + fi + return 0 } ''; diff --git a/nixos/modules/system/boot/stage-1.nix b/nixos/modules/system/boot/stage-1.nix index f58b68cb3353..f4cf9753c0a1 100644 --- a/nixos/modules/system/boot/stage-1.nix +++ b/nixos/modules/system/boot/stage-1.nix @@ -251,9 +251,9 @@ let postInstall = '' echo checking syntax # check both with bash - ${pkgs.bash}/bin/sh -n $target + ${pkgs.buildPackages.bash}/bin/sh -n $target # and with ash shell, just in case - ${extraUtils}/bin/ash -n $target + ${pkgs.buildPackages.busybox}/bin/ash -n $target ''; inherit udevRules extraUtils modulesClosure; diff --git a/nixos/modules/system/boot/stage-2-init.sh b/nixos/modules/system/boot/stage-2-init.sh index 49764b75a557..03daafa1ce4f 100644 --- a/nixos/modules/system/boot/stage-2-init.sh +++ b/nixos/modules/system/boot/stage-2-init.sh @@ -152,6 +152,14 @@ ln -sfn /run/booted-system /nix/var/nix/gcroots/booted-system @shell@ @postBootCommands@ +# Ensure systemd doesn't try to populate /etc, by forcing its first-boot +# heuristic off. It doesn't matter what's in /etc/machine-id for this purpose, +# and systemd will immediately fill in the file when it starts, so just +# creating it is enough. This `: >>` pattern avoids forking and avoids changing +# the mtime if the file already exists. +: >> /etc/machine-id + + # Reset the logging file descriptors. exec 1>&$logOutFd 2>&$logErrFd exec {logOutFd}>&- {logErrFd}>&- diff --git a/nixos/modules/system/boot/systemd-lib.nix b/nixos/modules/system/boot/systemd-lib.nix index 9c8d4a026b4a..68a40377ee13 100644 --- a/nixos/modules/system/boot/systemd-lib.nix +++ b/nixos/modules/system/boot/systemd-lib.nix @@ -63,7 +63,7 @@ in rec { assertValueOneOf = name: values: group: attr: optional (attr ? ${name} && !elem attr.${name} values) - "Systemd ${group} field `${name}' cannot have value `${attr.${name}}'."; + "Systemd ${group} field `${name}' cannot have value `${toString attr.${name}}'."; assertHasField = name: group: attr: optional (!(attr ? ${name})) diff --git a/nixos/modules/system/boot/systemd-unit-options.nix b/nixos/modules/system/boot/systemd-unit-options.nix index 2cff25a8c854..5f2bec5c34ae 100644 --- a/nixos/modules/system/boot/systemd-unit-options.nix +++ b/nixos/modules/system/boot/systemd-unit-options.nix @@ -394,7 +394,7 @@ in rec { Each attribute in this set specifies an option in the <literal>[Timer]</literal> section of the unit. See <citerefentry><refentrytitle>systemd.timer</refentrytitle> - <manvolnum>7</manvolnum></citerefentry> and + <manvolnum>5</manvolnum></citerefentry> and <citerefentry><refentrytitle>systemd.time</refentrytitle> <manvolnum>7</manvolnum></citerefentry> for details. ''; diff --git a/nixos/modules/system/boot/systemd.nix b/nixos/modules/system/boot/systemd.nix index 3ac4c02b61f5..a1412bc32904 100644 --- a/nixos/modules/system/boot/systemd.nix +++ b/nixos/modules/system/boot/systemd.nix @@ -747,6 +747,7 @@ in "systemd/journald.conf".text = '' [Journal] + Storage=persistent RateLimitInterval=${config.services.journald.rateLimitInterval} RateLimitBurst=${toString config.services.journald.rateLimitBurst} ${optionalString (config.services.journald.console != "") '' @@ -783,19 +784,6 @@ in services.dbus.enable = true; - system.activationScripts.systemd = stringAfter [ "groups" ] - '' - mkdir -m 0755 -p /var/lib/udev - - if ! [ -e /etc/machine-id ]; then - ${systemd}/bin/systemd-machine-id-setup - fi - - # Keep a persistent journal. Note that systemd-tmpfiles will - # set proper ownership/permissions. - mkdir -m 0700 -p /var/log/journal - ''; - users.users.systemd-network.uid = config.ids.uids.systemd-network; users.groups.systemd-network.gid = config.ids.gids.systemd-network; users.users.systemd-resolve.uid = config.ids.uids.systemd-resolve; diff --git a/nixos/modules/tasks/network-interfaces-scripted.nix b/nixos/modules/tasks/network-interfaces-scripted.nix index af61c95da0af..93dfefdce902 100644 --- a/nixos/modules/tasks/network-interfaces-scripted.nix +++ b/nixos/modules/tasks/network-interfaces-scripted.nix @@ -85,7 +85,8 @@ let after = [ "network-pre.target" "systemd-udevd.service" "systemd-sysctl.service" ]; before = [ "network.target" "shutdown.target" ]; wants = [ "network.target" ]; - partOf = map (i: "network-addresses-${i.name}.service") interfaces; + # exclude bridges from the partOf relationship to fix container networking bug #47210 + partOf = map (i: "network-addresses-${i.name}.service") (filter (i: !(hasAttr i.name cfg.bridges)) interfaces); conflicts = [ "shutdown.target" ]; wantedBy = [ "multi-user.target" ] ++ optional hasDefaultGatewaySet "network-online.target"; diff --git a/nixos/modules/virtualisation/container-config.nix b/nixos/modules/virtualisation/container-config.nix index 5e368acd6d8b..561db7cabcfb 100644 --- a/nixos/modules/virtualisation/container-config.nix +++ b/nixos/modules/virtualisation/container-config.nix @@ -22,6 +22,13 @@ with lib; # Not supported in systemd-nspawn containers. security.audit.enable = false; + # Make sure that root user in container will talk to host nix-daemon + environment.etc."profile".text = '' + export NIX_REMOTE=daemon + ''; + + + }; } diff --git a/nixos/modules/virtualisation/hyperv-guest.nix b/nixos/modules/virtualisation/hyperv-guest.nix index ecd2a8117710..0f1f052880c5 100644 --- a/nixos/modules/virtualisation/hyperv-guest.nix +++ b/nixos/modules/virtualisation/hyperv-guest.nix @@ -9,20 +9,47 @@ in { options = { virtualisation.hypervGuest = { enable = mkEnableOption "Hyper-V Guest Support"; + + videoMode = mkOption { + type = types.str; + default = "1152x864"; + example = "1024x768"; + description = '' + Resolution at which to initialize the video adapter. + + Supports screen resolution up to Full HD 1920x1080 with 32 bit color + on Windows Server 2012, and 1600x1200 with 16 bit color on Windows + Server 2008 R2 or earlier. + ''; + }; }; }; config = mkIf cfg.enable { + boot = { + initrd.kernelModules = [ + "hv_balloon" "hv_netvsc" "hv_storvsc" "hv_utils" "hv_vmbus" + ]; + + kernelParams = [ + "video=hyperv_fb:${cfg.videoMode}" + ]; + }; + environment.systemPackages = [ config.boot.kernelPackages.hyperv-daemons.bin ]; security.rngd.enable = false; - # enable hotadding memory + # enable hotadding cpu/memory services.udev.packages = lib.singleton (pkgs.writeTextFile { - name = "hyperv-memory-hotadd-udev-rules"; - destination = "/etc/udev/rules.d/99-hyperv-memory-hotadd.rules"; + name = "hyperv-cpu-and-memory-hotadd-udev-rules"; + destination = "/etc/udev/rules.d/99-hyperv-cpu-and-memory-hotadd.rules"; text = '' - ACTION="add", SUBSYSTEM=="memory", ATTR{state}="online" + # Memory hotadd + SUBSYSTEM=="memory", ACTION=="add", DEVPATH=="/devices/system/memory/memory[0-9]*", TEST=="state", ATTR{state}="online" + + # CPU hotadd + SUBSYSTEM=="cpu", ACTION=="add", DEVPATH=="/devices/system/cpu/cpu[0-9]*", TEST=="online", ATTR{online}="1" ''; }); diff --git a/nixos/modules/virtualisation/qemu-guest-agent.nix b/nixos/modules/virtualisation/qemu-guest-agent.nix index e0d2b3dc509d..665224e35d8c 100644 --- a/nixos/modules/virtualisation/qemu-guest-agent.nix +++ b/nixos/modules/virtualisation/qemu-guest-agent.nix @@ -25,7 +25,7 @@ in { systemd.services.qemu-guest-agent = { description = "Run the QEMU Guest Agent"; serviceConfig = { - ExecStart = "${pkgs.kvm.ga}/bin/qemu-ga"; + ExecStart = "${pkgs.qemu.ga}/bin/qemu-ga"; Restart = "always"; RestartSec = 0; }; diff --git a/nixos/modules/virtualisation/xe-guest-utilities.nix b/nixos/modules/virtualisation/xe-guest-utilities.nix index d703353858c0..675cf9297371 100644 --- a/nixos/modules/virtualisation/xe-guest-utilities.nix +++ b/nixos/modules/virtualisation/xe-guest-utilities.nix @@ -5,7 +5,7 @@ let in { options = { services.xe-guest-utilities = { - enable = mkEnableOption "Whether to enable the Xen guest utilities daemon."; + enable = mkEnableOption "the Xen guest utilities daemon"; }; }; config = mkIf cfg.enable { diff --git a/nixos/release.nix b/nixos/release.nix index e53ebff9b6dc..5412080cca18 100644 --- a/nixos/release.nix +++ b/nixos/release.nix @@ -250,13 +250,14 @@ in rec { tests.acme = callTest tests/acme.nix {}; tests.avahi = callTest tests/avahi.nix {}; tests.beegfs = callTest tests/beegfs.nix {}; + tests.upnp = callTest tests/upnp.nix {}; tests.bittorrent = callTest tests/bittorrent.nix {}; tests.bind = callTest tests/bind.nix {}; #tests.blivet = callTest tests/blivet.nix {}; # broken since 2017-07024 tests.boot = callSubTests tests/boot.nix {}; tests.boot-stage1 = callTest tests/boot-stage1.nix {}; tests.borgbackup = callTest tests/borgbackup.nix {}; - tests.buildbot = callTest tests/buildbot.nix {}; + tests.buildbot = callSubTests tests/buildbot.nix {}; tests.cadvisor = callTestOnMatchingSystems ["x86_64-linux"] tests/cadvisor.nix {}; tests.ceph = callTestOnMatchingSystems ["x86_64-linux"] tests/ceph.nix {}; tests.certmgr = callSubTests tests/certmgr.nix {}; @@ -362,6 +363,7 @@ in rec { tests.netdata = callTest tests/netdata.nix { }; tests.networking.networkd = callSubTests tests/networking.nix { networkd = true; }; tests.networking.scripted = callSubTests tests/networking.nix { networkd = false; }; + tests.nextcloud = callSubTests tests/nextcloud { }; # TODO: put in networking.nix after the test becomes more complete tests.networkingProxy = callTest tests/networking-proxy.nix {}; tests.nexus = callTest tests/nexus.nix { }; @@ -388,12 +390,14 @@ in rec { tests.predictable-interface-names = callSubTests tests/predictable-interface-names.nix {}; tests.printing = callTest tests/printing.nix {}; tests.prometheus = callTest tests/prometheus.nix {}; + tests.prometheus-exporters = callTest tests/prometheus-exporters.nix {}; tests.prosody = callTest tests/prosody.nix {}; tests.proxy = callTest tests/proxy.nix {}; tests.quagga = callTest tests/quagga.nix {}; tests.quake3 = callTest tests/quake3.nix {}; tests.rabbitmq = callTest tests/rabbitmq.nix {}; tests.radicale = callTest tests/radicale.nix {}; + tests.redmine = callTest tests/redmine.nix {}; tests.rspamd = callSubTests tests/rspamd.nix {}; tests.runInMachine = callTest tests/run-in-machine.nix {}; tests.rxe = callTest tests/rxe.nix {}; diff --git a/nixos/tests/bittorrent.nix b/nixos/tests/bittorrent.nix index 609b1ff7a83a..8977be9b859f 100644 --- a/nixos/tests/bittorrent.nix +++ b/nixos/tests/bittorrent.nix @@ -13,57 +13,95 @@ let # Some random file to serve. file = pkgs.hello.src; - miniupnpdConf = nodes: pkgs.writeText "miniupnpd.conf" - '' - ext_ifname=eth1 - listening_ip=${(pkgs.lib.head nodes.router.config.networking.interfaces.eth2.ipv4.addresses).address}/24 - allow 1024-65535 192.168.2.0/24 1024-65535 - ''; - + internalRouterAddress = "192.168.3.1"; + internalClient1Address = "192.168.3.2"; + externalRouterAddress = "80.100.100.1"; + externalClient2Address = "80.100.100.2"; + externalTrackerAddress = "80.100.100.3"; in { name = "bittorrent"; meta = with pkgs.stdenv.lib.maintainers; { - maintainers = [ domenkozar eelco chaoflow rob wkennington ]; + maintainers = [ domenkozar eelco chaoflow rob wkennington bobvanderlinden ]; }; nodes = { tracker = { pkgs, ... }: - { environment.systemPackages = [ pkgs.transmission pkgs.opentracker ]; + { environment.systemPackages = [ pkgs.transmission ]; + + virtualisation.vlans = [ 1 ]; + networking.interfaces.eth1.ipv4.addresses = [ + { address = externalTrackerAddress; prefixLength = 24; } + ]; # We need Apache on the tracker to serve the torrents. services.httpd.enable = true; services.httpd.adminAddr = "foo@example.org"; services.httpd.documentRoot = "/tmp"; - networking.firewall.enable = false; # FIXME: figure out what ports we actually need + networking.firewall.enable = false; + + services.opentracker.enable = true; + + services.transmission.enable = true; + services.transmission.settings.dht-enabled = false; + services.transmission.settings.port-forwaring-enabled = false; }; router = - { pkgs, ... }: - { environment.systemPackages = [ pkgs.miniupnpd ]; - virtualisation.vlans = [ 1 2 ]; + { pkgs, nodes, ... }: + { virtualisation.vlans = [ 1 2 ]; networking.nat.enable = true; networking.nat.internalInterfaces = [ "eth2" ]; networking.nat.externalInterface = "eth1"; - networking.firewall.enable = false; + networking.firewall.enable = true; + networking.firewall.trustedInterfaces = [ "eth2" ]; + networking.interfaces.eth0.ipv4.addresses = []; + networking.interfaces.eth1.ipv4.addresses = [ + { address = externalRouterAddress; prefixLength = 24; } + ]; + networking.interfaces.eth2.ipv4.addresses = [ + { address = internalRouterAddress; prefixLength = 24; } + ]; + services.miniupnpd = { + enable = true; + externalInterface = "eth1"; + internalIPs = [ "eth2" ]; + appendConfig = '' + ext_ip=${externalRouterAddress} + ''; + }; }; client1 = { pkgs, nodes, ... }: - { environment.systemPackages = [ pkgs.transmission ]; + { environment.systemPackages = [ pkgs.transmission pkgs.miniupnpc ]; virtualisation.vlans = [ 2 ]; - networking.defaultGateway = - (pkgs.lib.head nodes.router.config.networking.interfaces.eth2.ipv4.addresses).address; + networking.interfaces.eth0.ipv4.addresses = []; + networking.interfaces.eth1.ipv4.addresses = [ + { address = internalClient1Address; prefixLength = 24; } + ]; + networking.defaultGateway = internalRouterAddress; networking.firewall.enable = false; + services.transmission.enable = true; + services.transmission.settings.dht-enabled = false; + services.transmission.settings.message-level = 3; }; client2 = { pkgs, ... }: { environment.systemPackages = [ pkgs.transmission ]; + virtualisation.vlans = [ 1 ]; + networking.interfaces.eth0.ipv4.addresses = []; + networking.interfaces.eth1.ipv4.addresses = [ + { address = externalClient2Address; prefixLength = 24; } + ]; networking.firewall.enable = false; + services.transmission.enable = true; + services.transmission.settings.dht-enabled = false; + services.transmission.settings.port-forwaring-enabled = false; }; }; @@ -72,43 +110,38 @@ in '' startAll; - # Enable NAT on the router and start miniupnpd. - $router->waitForUnit("nat"); - $router->succeed( - "iptables -w -t nat -N MINIUPNPD", - "iptables -w -t nat -A PREROUTING -i eth1 -j MINIUPNPD", - "echo 1 > /proc/sys/net/ipv4/ip_forward", - "miniupnpd -f ${miniupnpdConf nodes}" - ); + # Wait for network and miniupnpd. + $router->waitForUnit("network-online.target"); + $router->waitForUnit("miniupnpd"); # Create the torrent. $tracker->succeed("mkdir /tmp/data"); $tracker->succeed("cp ${file} /tmp/data/test.tar.bz2"); - $tracker->succeed("transmission-create /tmp/data/test.tar.bz2 -p -t http://${(pkgs.lib.head nodes.tracker.config.networking.interfaces.eth1.ipv4.addresses).address}:6969/announce -o /tmp/test.torrent"); + $tracker->succeed("transmission-create /tmp/data/test.tar.bz2 --private --tracker http://${externalTrackerAddress}:6969/announce --outfile /tmp/test.torrent"); $tracker->succeed("chmod 644 /tmp/test.torrent"); # Start the tracker. !!! use a less crappy tracker - $tracker->waitForUnit("network.target"); - $tracker->succeed("opentracker -p 6969 >&2 &"); + $tracker->waitForUnit("network-online.target"); + $tracker->waitForUnit("opentracker.service"); $tracker->waitForOpenPort(6969); # Start the initial seeder. - my $pid = $tracker->succeed("transmission-cli /tmp/test.torrent -M -w /tmp/data >&2 & echo \$!"); + $tracker->succeed("transmission-remote --add /tmp/test.torrent --no-portmap --no-dht --download-dir /tmp/data"); # Now we should be able to download from the client behind the NAT. $tracker->waitForUnit("httpd"); - $client1->waitForUnit("network.target"); - $client1->succeed("transmission-cli http://tracker/test.torrent -w /tmp >&2 &"); + $client1->waitForUnit("network-online.target"); + $client1->succeed("transmission-remote --add http://${externalTrackerAddress}/test.torrent --download-dir /tmp >&2 &"); $client1->waitForFile("/tmp/test.tar.bz2"); $client1->succeed("cmp /tmp/test.tar.bz2 ${file}"); # Bring down the initial seeder. - $tracker->succeed("kill -9 $pid"); + # $tracker->stopJob("transmission"); # Now download from the second client. This can only succeed if # the first client created a NAT hole in the router. - $client2->waitForUnit("network.target"); - $client2->succeed("transmission-cli http://tracker/test.torrent -M -w /tmp >&2 &"); + $client2->waitForUnit("network-online.target"); + $client2->succeed("transmission-remote --add http://${externalTrackerAddress}/test.torrent --no-portmap --no-dht --download-dir /tmp >&2 &"); $client2->waitForFile("/tmp/test.tar.bz2"); $client2->succeed("cmp /tmp/test.tar.bz2 ${file}"); ''; diff --git a/nixos/tests/buildbot.nix b/nixos/tests/buildbot.nix index cf408dc7fec9..399fd39005e2 100644 --- a/nixos/tests/buildbot.nix +++ b/nixos/tests/buildbot.nix @@ -1,111 +1,117 @@ -# Test ensures buildbot master comes up correctly and workers can connect - -import ./make-test.nix ({ pkgs, ... } : { - name = "buildbot"; - - nodes = { - bbmaster = { pkgs, ... }: { - services.buildbot-master = { - enable = true; - package = pkgs.buildbot-full; - - # NOTE: use fake repo due to no internet in hydra ci - factorySteps = [ - "steps.Git(repourl='git://gitrepo/fakerepo.git', mode='incremental')" - "steps.ShellCommand(command=['bash', 'fakerepo.sh'])" - ]; - changeSource = [ - "changes.GitPoller('git://gitrepo/fakerepo.git', workdir='gitpoller-workdir', branch='master', pollinterval=300)" - ]; +{ system ? builtins.currentSystem }: + +with import ../lib/testing.nix { inherit system; }; + +let + # Test ensures buildbot master comes up correctly and workers can connect + mkBuildbotTest = python: makeTest { + name = "buildbot"; + + nodes = { + bbmaster = { pkgs, ... }: { + services.buildbot-master = { + enable = true; + package = python.pkgs.buildbot-full; + + # NOTE: use fake repo due to no internet in hydra ci + factorySteps = [ + "steps.Git(repourl='git://gitrepo/fakerepo.git', mode='incremental')" + "steps.ShellCommand(command=['bash', 'fakerepo.sh'])" + ]; + changeSource = [ + "changes.GitPoller('git://gitrepo/fakerepo.git', workdir='gitpoller-workdir', branch='master', pollinterval=300)" + ]; + }; + networking.firewall.allowedTCPPorts = [ 8010 8011 9989 ]; + environment.systemPackages = with pkgs; [ git python.pkgs.buildbot-full ]; }; - networking.firewall.allowedTCPPorts = [ 8010 8011 9989 ]; - environment.systemPackages = with pkgs; [ git buildbot-full ]; - }; - bbworker = { pkgs, ... }: { - services.buildbot-worker = { - enable = true; - masterUrl = "bbmaster:9989"; + bbworker = { pkgs, ... }: { + services.buildbot-worker = { + enable = true; + masterUrl = "bbmaster:9989"; + }; + environment.systemPackages = with pkgs; [ git python.pkgs.buildbot-worker ]; }; - environment.systemPackages = with pkgs; [ git buildbot-worker ]; - }; - gitrepo = { pkgs, ... }: { - services.openssh.enable = true; - networking.firewall.allowedTCPPorts = [ 22 9418 ]; - environment.systemPackages = with pkgs; [ git ]; + gitrepo = { pkgs, ... }: { + services.openssh.enable = true; + networking.firewall.allowedTCPPorts = [ 22 9418 ]; + environment.systemPackages = with pkgs; [ git ]; + }; }; - }; - testScript = '' - #Start up and populate fake repo - $gitrepo->waitForUnit("multi-user.target"); - print($gitrepo->execute(" \ - git config --global user.name 'Nobody Fakeuser' && \ - git config --global user.email 'nobody\@fakerepo.com' && \ - rm -rvf /srv/repos/fakerepo.git /tmp/fakerepo && \ - mkdir -pv /srv/repos/fakerepo ~/.ssh && \ - ssh-keyscan -H gitrepo > ~/.ssh/known_hosts && \ - cat ~/.ssh/known_hosts && \ - cd /srv/repos/fakerepo && \ - git init && \ - echo -e '#!/bin/sh\necho fakerepo' > fakerepo.sh && \ - cat fakerepo.sh && \ - touch .git/git-daemon-export-ok && \ - git add fakerepo.sh .git/git-daemon-export-ok && \ - git commit -m fakerepo && \ - git daemon --verbose --export-all --base-path=/srv/repos --reuseaddr & \ - ")); - - # Test gitrepo - $bbmaster->waitForUnit("network-online.target"); - #$bbmaster->execute("nc -z gitrepo 9418"); - print($bbmaster->execute(" \ - rm -rfv /tmp/fakerepo && \ - git clone git://gitrepo/fakerepo /tmp/fakerepo && \ - pwd && \ - ls -la && \ - ls -la /tmp/fakerepo \ - ")); - - # Test start master and connect worker - $bbmaster->waitForUnit("buildbot-master.service"); - $bbmaster->waitUntilSucceeds("curl -s --head http://bbmaster:8010") =~ /200 OK/; - $bbworker->waitForUnit("network-online.target"); - $bbworker->execute("nc -z bbmaster 8010"); - $bbworker->execute("nc -z bbmaster 9989"); - $bbworker->waitForUnit("buildbot-worker.service"); - print($bbworker->execute("ls -la /home/bbworker/worker")); - - - # Test stop buildbot master and worker - print($bbmaster->execute(" \ - systemctl -l --no-pager status buildbot-master && \ - systemctl stop buildbot-master \ - ")); - $bbworker->fail("nc -z bbmaster 8010"); - $bbworker->fail("nc -z bbmaster 9989"); - print($bbworker->execute(" \ - systemctl -l --no-pager status buildbot-worker && \ - systemctl stop buildbot-worker && \ - ls -la /home/bbworker/worker \ - ")); - - - # Test buildbot daemon mode - # NOTE: daemon mode tests disabled due to broken PYTHONPATH child inheritence - # - #$bbmaster->execute("buildbot create-master /tmp"); - #$bbmaster->execute("mv -fv /tmp/master.cfg.sample /tmp/master.cfg"); - #$bbmaster->execute("sed -i 's/8010/8011/' /tmp/master.cfg"); - #$bbmaster->execute("buildbot start /tmp"); - #$bbworker->execute("nc -z bbmaster 8011"); - #$bbworker->waitUntilSucceeds("curl -s --head http://bbmaster:8011") =~ /200 OK/; - #$bbmaster->execute("buildbot stop /tmp"); - #$bbworker->fail("nc -z bbmaster 8011"); - - ''; - - meta.maintainers = with pkgs.stdenv.lib.maintainers; [ nand0p ]; - -}) + testScript = '' + #Start up and populate fake repo + $gitrepo->waitForUnit("multi-user.target"); + print($gitrepo->execute(" \ + git config --global user.name 'Nobody Fakeuser' && \ + git config --global user.email 'nobody\@fakerepo.com' && \ + rm -rvf /srv/repos/fakerepo.git /tmp/fakerepo && \ + mkdir -pv /srv/repos/fakerepo ~/.ssh && \ + ssh-keyscan -H gitrepo > ~/.ssh/known_hosts && \ + cat ~/.ssh/known_hosts && \ + cd /srv/repos/fakerepo && \ + git init && \ + echo -e '#!/bin/sh\necho fakerepo' > fakerepo.sh && \ + cat fakerepo.sh && \ + touch .git/git-daemon-export-ok && \ + git add fakerepo.sh .git/git-daemon-export-ok && \ + git commit -m fakerepo && \ + git daemon --verbose --export-all --base-path=/srv/repos --reuseaddr & \ + ")); + + # Test gitrepo + $bbmaster->waitForUnit("network-online.target"); + #$bbmaster->execute("nc -z gitrepo 9418"); + print($bbmaster->execute(" \ + rm -rfv /tmp/fakerepo && \ + git clone git://gitrepo/fakerepo /tmp/fakerepo && \ + pwd && \ + ls -la && \ + ls -la /tmp/fakerepo \ + ")); + + # Test start master and connect worker + $bbmaster->waitForUnit("buildbot-master.service"); + $bbmaster->waitUntilSucceeds("curl -s --head http://bbmaster:8010") =~ /200 OK/; + $bbworker->waitForUnit("network-online.target"); + $bbworker->execute("nc -z bbmaster 8010"); + $bbworker->execute("nc -z bbmaster 9989"); + $bbworker->waitForUnit("buildbot-worker.service"); + print($bbworker->execute("ls -la /home/bbworker/worker")); + + + # Test stop buildbot master and worker + print($bbmaster->execute(" \ + systemctl -l --no-pager status buildbot-master && \ + systemctl stop buildbot-master \ + ")); + $bbworker->fail("nc -z bbmaster 8010"); + $bbworker->fail("nc -z bbmaster 9989"); + print($bbworker->execute(" \ + systemctl -l --no-pager status buildbot-worker && \ + systemctl stop buildbot-worker && \ + ls -la /home/bbworker/worker \ + ")); + + + # Test buildbot daemon mode + $bbmaster->execute("buildbot create-master /tmp"); + $bbmaster->execute("mv -fv /tmp/master.cfg.sample /tmp/master.cfg"); + $bbmaster->execute("sed -i 's/8010/8011/' /tmp/master.cfg"); + $bbmaster->execute("buildbot start /tmp"); + $bbworker->execute("nc -z bbmaster 8011"); + $bbworker->waitUntilSucceeds("curl -s --head http://bbmaster:8011") =~ /200 OK/; + $bbmaster->execute("buildbot stop /tmp"); + $bbworker->fail("nc -z bbmaster 8011"); + + ''; + + meta.maintainers = with pkgs.stdenv.lib.maintainers; [ nand0p ]; + + }; +in { + python2 = mkBuildbotTest pkgs.python2; + python3 = mkBuildbotTest pkgs.python3; +} diff --git a/nixos/tests/codimd.nix b/nixos/tests/codimd.nix index 9dedac96844a..562f6f24f999 100644 --- a/nixos/tests/codimd.nix +++ b/nixos/tests/codimd.nix @@ -40,8 +40,7 @@ import ./make-test.nix ({ pkgs, lib, ... }: subtest "CodiMD sqlite", sub { $codimdSqlite->waitForUnit("codimd.service"); $codimdSqlite->waitForOpenPort(3000); - $codimdSqlite->sleep(10); # avoid 503 during startup - $codimdSqlite->succeed("curl -sSf http://localhost:3000/new"); + $codimdSqlite->waitUntilSucceeds("curl -sSf http://localhost:3000/new"); }; subtest "CodiMD postgres", sub { @@ -49,8 +48,7 @@ import ./make-test.nix ({ pkgs, lib, ... }: $codimdPostgres->waitForUnit("codimd.service"); $codimdPostgres->waitForOpenPort(5432); $codimdPostgres->waitForOpenPort(3000); - $codimdPostgres->sleep(10); # avoid 503 during startup - $codimdPostgres->succeed("curl -sSf http://localhost:3000/new"); + $codimdPostgres->waitUntilSucceeds("curl -sSf http://localhost:3000/new"); }; ''; }) diff --git a/nixos/tests/containers-imperative.nix b/nixos/tests/containers-imperative.nix index 6f86819f4e88..782095a09dad 100644 --- a/nixos/tests/containers-imperative.nix +++ b/nixos/tests/containers-imperative.nix @@ -86,6 +86,9 @@ import ./make-test.nix ({ pkgs, ...} : { # Execute commands via the root shell. $machine->succeed("nixos-container run $id1 -- uname") =~ /Linux/ or die; + # Execute a nix command via the root shell. (regression test for #40355) + $machine->succeed("nixos-container run $id1 -- nix-instantiate -E 'derivation { name = \"empty\"; builder = \"false\"; system = \"false\"; }'"); + # Stop and start (regression test for #4989) $machine->succeed("nixos-container stop $id1"); $machine->succeed("nixos-container start $id1"); diff --git a/nixos/tests/misc.nix b/nixos/tests/misc.nix index 6d72ac997f8d..3ad55651b112 100644 --- a/nixos/tests/misc.nix +++ b/nixos/tests/misc.nix @@ -78,6 +78,8 @@ import ./make-test.nix ({ pkgs, ...} : rec { # Test whether we have a reboot record in wtmp. subtest "reboot-wtmp", sub { + $machine->shutdown; + $machine->waitForUnit('multi-user.target'); $machine->succeed("last | grep reboot >&2"); }; diff --git a/nixos/tests/nextcloud/basic.nix b/nixos/tests/nextcloud/basic.nix new file mode 100644 index 000000000000..c3b710f0f904 --- /dev/null +++ b/nixos/tests/nextcloud/basic.nix @@ -0,0 +1,56 @@ +import ../make-test.nix ({ pkgs, ...}: let + adminpass = "notproduction"; + adminuser = "root"; +in { + name = "nextcloud-basic"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ globin eqyiel ]; + }; + + nodes = { + # The only thing the client needs to do is download a file. + client = { ... }: {}; + + nextcloud = { config, pkgs, ... }: { + networking.firewall.allowedTCPPorts = [ 80 ]; + + services.nextcloud = { + enable = true; + nginx.enable = true; + hostName = "nextcloud"; + config = { + # Don't inherit adminuser since "root" is supposed to be the default + inherit adminpass; + }; + }; + }; + }; + + testScript = let + withRcloneEnv = pkgs.writeScript "with-rclone-env" '' + #!${pkgs.stdenv.shell} + export RCLONE_CONFIG_NEXTCLOUD_TYPE=webdav + export RCLONE_CONFIG_NEXTCLOUD_URL="http://nextcloud/remote.php/webdav/" + export RCLONE_CONFIG_NEXTCLOUD_VENDOR="nextcloud" + export RCLONE_CONFIG_NEXTCLOUD_USER="${adminuser}" + export RCLONE_CONFIG_NEXTCLOUD_PASS="$(${pkgs.rclone}/bin/rclone obscure ${adminpass})" + "''${@}" + ''; + copySharedFile = pkgs.writeScript "copy-shared-file" '' + #!${pkgs.stdenv.shell} + echo 'hi' | ${withRcloneEnv} ${pkgs.rclone}/bin/rclone rcat nextcloud:test-shared-file + ''; + + diffSharedFile = pkgs.writeScript "diff-shared-file" '' + #!${pkgs.stdenv.shell} + diff <(echo 'hi') <(${pkgs.rclone}/bin/rclone cat nextcloud:test-shared-file) + ''; + in '' + startAll(); + $nextcloud->waitForUnit("multi-user.target"); + $nextcloud->succeed("curl -sSf http://nextcloud/login"); + $nextcloud->succeed("${withRcloneEnv} ${copySharedFile}"); + $client->waitForUnit("multi-user.target"); + $client->succeed("${withRcloneEnv} ${diffSharedFile}"); + ''; +}) diff --git a/nixos/tests/nextcloud/default.nix b/nixos/tests/nextcloud/default.nix new file mode 100644 index 000000000000..66da6794b961 --- /dev/null +++ b/nixos/tests/nextcloud/default.nix @@ -0,0 +1,6 @@ +{ system ? builtins.currentSystem }: +{ + basic = import ./basic.nix { inherit system; }; + with-postgresql-and-redis = import ./with-postgresql-and-redis.nix { inherit system; }; + with-mysql-and-memcached = import ./with-mysql-and-memcached.nix { inherit system; }; +} diff --git a/nixos/tests/nextcloud/with-mysql-and-memcached.nix b/nixos/tests/nextcloud/with-mysql-and-memcached.nix new file mode 100644 index 000000000000..c0d347238b47 --- /dev/null +++ b/nixos/tests/nextcloud/with-mysql-and-memcached.nix @@ -0,0 +1,97 @@ +import ../make-test.nix ({ pkgs, ...}: let + adminpass = "hunter2"; + adminuser = "root"; +in { + name = "nextcloud-with-mysql-and-memcached"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ eqyiel ]; + }; + + nodes = { + # The only thing the client needs to do is download a file. + client = { ... }: {}; + + nextcloud = { config, pkgs, ... }: { + networking.firewall.allowedTCPPorts = [ 80 ]; + + services.nextcloud = { + enable = true; + hostName = "nextcloud"; + nginx.enable = true; + https = true; + caching = { + apcu = true; + redis = false; + memcached = true; + }; + config = { + dbtype = "mysql"; + dbname = "nextcloud"; + dbuser = "nextcloud"; + dbhost = "127.0.0.1"; + dbport = 3306; + dbpass = "hunter2"; + # Don't inherit adminuser since "root" is supposed to be the default + inherit adminpass; + }; + }; + + services.mysql = { + enable = true; + bind = "127.0.0.1"; + package = pkgs.mariadb; + initialScript = pkgs.writeText "mysql-init" '' + CREATE USER 'nextcloud'@'localhost' IDENTIFIED BY 'hunter2'; + CREATE DATABASE IF NOT EXISTS nextcloud; + GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER, + CREATE TEMPORARY TABLES ON nextcloud.* TO 'nextcloud'@'localhost' + IDENTIFIED BY 'hunter2'; + FLUSH privileges; + ''; + }; + + systemd.services."nextcloud-setup"= { + requires = ["mysql.service"]; + after = ["mysql.service"]; + }; + + services.memcached.enable = true; + }; + }; + + testScript = let + configureMemcached = pkgs.writeScript "configure-memcached" '' + #!${pkgs.stdenv.shell} + nextcloud-occ config:system:set memcached_servers 0 0 --value 127.0.0.1 --type string + nextcloud-occ config:system:set memcached_servers 0 1 --value 11211 --type integer + nextcloud-occ config:system:set memcache.local --value '\OC\Memcache\APCu' --type string + nextcloud-occ config:system:set memcache.distributed --value '\OC\Memcache\Memcached' --type string + ''; + withRcloneEnv = pkgs.writeScript "with-rclone-env" '' + #!${pkgs.stdenv.shell} + export RCLONE_CONFIG_NEXTCLOUD_TYPE=webdav + export RCLONE_CONFIG_NEXTCLOUD_URL="http://nextcloud/remote.php/webdav/" + export RCLONE_CONFIG_NEXTCLOUD_VENDOR="nextcloud" + export RCLONE_CONFIG_NEXTCLOUD_USER="${adminuser}" + export RCLONE_CONFIG_NEXTCLOUD_PASS="$(${pkgs.rclone}/bin/rclone obscure ${adminpass})" + ''; + copySharedFile = pkgs.writeScript "copy-shared-file" '' + #!${pkgs.stdenv.shell} + echo 'hi' | ${pkgs.rclone}/bin/rclone rcat nextcloud:test-shared-file + ''; + + diffSharedFile = pkgs.writeScript "diff-shared-file" '' + #!${pkgs.stdenv.shell} + diff <(echo 'hi') <(${pkgs.rclone}/bin/rclone cat nextcloud:test-shared-file) + ''; + in '' + startAll(); + $nextcloud->waitForUnit("multi-user.target"); + $nextcloud->succeed("${configureMemcached}"); + $nextcloud->succeed("curl -sSf http://nextcloud/login"); + $nextcloud->succeed("${withRcloneEnv} ${copySharedFile}"); + $client->waitForUnit("multi-user.target"); + $client->succeed("${withRcloneEnv} ${diffSharedFile}"); + + ''; +}) diff --git a/nixos/tests/nextcloud/with-postgresql-and-redis.nix b/nixos/tests/nextcloud/with-postgresql-and-redis.nix new file mode 100644 index 000000000000..0351d4db69ac --- /dev/null +++ b/nixos/tests/nextcloud/with-postgresql-and-redis.nix @@ -0,0 +1,130 @@ +import ../make-test.nix ({ pkgs, ...}: let + adminpass = "hunter2"; + adminuser = "custom-admin-username"; +in { + name = "nextcloud-with-postgresql-and-redis"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ eqyiel ]; + }; + + nodes = { + # The only thing the client needs to do is download a file. + client = { ... }: {}; + + nextcloud = { config, pkgs, ... }: { + networking.firewall.allowedTCPPorts = [ 80 ]; + + services.nextcloud = { + enable = true; + hostName = "nextcloud"; + nginx.enable = true; + caching = { + apcu = false; + redis = true; + memcached = false; + }; + config = { + dbtype = "pgsql"; + dbname = "nextcloud"; + dbuser = "nextcloud"; + dbhost = "localhost"; + dbpassFile = toString (pkgs.writeText "db-pass-file" '' + hunter2 + ''); + inherit adminuser; + adminpassFile = toString (pkgs.writeText "admin-pass-file" '' + ${adminpass} + ''); + }; + }; + + services.redis = { + unixSocket = "/var/run/redis/redis.sock"; + enable = true; + extraConfig = '' + unixsocketperm 770 + ''; + }; + + systemd.services.redis = { + preStart = '' + mkdir -p /var/run/redis + chown ${config.services.redis.user}:${config.services.nginx.group} /var/run/redis + ''; + serviceConfig.PermissionsStartOnly = true; + }; + + systemd.services."nextcloud-setup"= { + requires = ["postgresql.service"]; + after = [ + "postgresql.service" + "chown-redis-socket.service" + ]; + }; + + # At the time of writing, redis creates its socket with the "nobody" + # group. I figure this is slightly less bad than making the socket world + # readable. + systemd.services."chown-redis-socket" = { + enable = true; + script = '' + until ${pkgs.redis}/bin/redis-cli ping; do + echo "waiting for redis..." + sleep 1 + done + chown ${config.services.redis.user}:${config.services.nginx.group} /var/run/redis/redis.sock + ''; + after = [ "redis.service" ]; + requires = [ "redis.service" ]; + wantedBy = [ "redis.service" ]; + serviceConfig = { + Type = "oneshot"; + }; + }; + + services.postgresql = { + enable = true; + initialScript = pkgs.writeText "psql-init" '' + create role nextcloud with login password 'hunter2'; + create database nextcloud with owner nextcloud; + ''; + }; + }; + }; + + testScript = let + configureRedis = pkgs.writeScript "configure-redis" '' + #!${pkgs.stdenv.shell} + nextcloud-occ config:system:set redis 'host' --value '/var/run/redis/redis.sock' --type string + nextcloud-occ config:system:set redis 'port' --value 0 --type integer + nextcloud-occ config:system:set memcache.local --value '\OC\Memcache\Redis' --type string + nextcloud-occ config:system:set memcache.locking --value '\OC\Memcache\Redis' --type string + ''; + withRcloneEnv = pkgs.writeScript "with-rclone-env" '' + #!${pkgs.stdenv.shell} + export RCLONE_CONFIG_NEXTCLOUD_TYPE=webdav + export RCLONE_CONFIG_NEXTCLOUD_URL="http://nextcloud/remote.php/webdav/" + export RCLONE_CONFIG_NEXTCLOUD_VENDOR="nextcloud" + export RCLONE_CONFIG_NEXTCLOUD_USER="${adminuser}" + export RCLONE_CONFIG_NEXTCLOUD_PASS="$(${pkgs.rclone}/bin/rclone obscure ${adminpass})" + "''${@}" + ''; + copySharedFile = pkgs.writeScript "copy-shared-file" '' + #!${pkgs.stdenv.shell} + echo 'hi' | ${pkgs.rclone}/bin/rclone rcat nextcloud:test-shared-file + ''; + + diffSharedFile = pkgs.writeScript "diff-shared-file" '' + #!${pkgs.stdenv.shell} + diff <(echo 'hi') <(${pkgs.rclone}/bin/rclone cat nextcloud:test-shared-file) + ''; + in '' + startAll(); + $nextcloud->waitForUnit("multi-user.target"); + $nextcloud->succeed("${configureRedis}"); + $nextcloud->succeed("curl -sSf http://nextcloud/login"); + $nextcloud->succeed("${withRcloneEnv} ${copySharedFile}"); + $client->waitForUnit("multi-user.target"); + $client->succeed("${withRcloneEnv} ${diffSharedFile}"); + ''; +}) diff --git a/nixos/tests/prometheus-exporters.nix b/nixos/tests/prometheus-exporters.nix new file mode 100644 index 000000000000..2f2c06dcb7d6 --- /dev/null +++ b/nixos/tests/prometheus-exporters.nix @@ -0,0 +1,297 @@ +import ./make-test.nix ({ lib, pkgs, ... }: +let + escape' = str: lib.replaceChars [''"'' "$" "\n"] [''\\\"'' "\\$" ""] str; + +/* + * The attrset `exporterTests` contains one attribute + * for each exporter test. Each of these attributes + * is expected to be an attrset containing: + * + * `exporterConfig`: + * this attribute set contains config for the exporter itself + * + * `exporterTest` + * this attribute set contains test instructions + * + * `metricProvider` (optional) + * this attribute contains additional machine config + * + * Example: + * exporterTests.<exporterName> = { + * exporterConfig = { + * enable = true; + * }; + * metricProvider = { + * services.<metricProvider>.enable = true; + * }; + * exporterTest = '' + * waitForUnit("prometheus-<exporterName>-exporter.service"); + * waitForOpenPort("1234"); + * succeed("curl -sSf 'localhost:1234/metrics'"); + * ''; + * }; + * + * # this would generate the following test config: + * + * nodes.<exporterName> = { + * services.prometheus.<exporterName> = { + * enable = true; + * }; + * services.<metricProvider>.enable = true; + * }; + * + * testScript = '' + * $<exporterName>->start(); + * $<exporterName>->waitForUnit("prometheus-<exporterName>-exporter.service"); + * $<exporterName>->waitForOpenPort("1234"); + * $<exporterName>->succeed("curl -sSf 'localhost:1234/metrics'"); + * $<exporterName>->shutdown(); + * ''; + */ + + exporterTests = { + + blackbox = { + exporterConfig = { + enable = true; + configFile = pkgs.writeText "config.yml" (builtins.toJSON { + modules.icmp_v6 = { + prober = "icmp"; + icmp.preferred_ip_protocol = "ip6"; + }; + }); + }; + exporterTest = '' + waitForUnit("prometheus-blackbox-exporter.service"); + waitForOpenPort(9115); + succeed("curl -sSf 'http://localhost:9115/probe?target=localhost&module=icmp_v6' | grep -q 'probe_success 1'"); + ''; + }; + + collectd = { + exporterConfig = { + enable = true; + extraFlags = [ "--web.collectd-push-path /collectd" ]; + }; + exporterTest =let postData = escape' '' + [{ + "values":[23], + "dstypes":["gauge"], + "type":"gauge", + "interval":1000, + "host":"testhost", + "plugin":"testplugin", + "time":$(date +%s) + }] + ''; in '' + waitForUnit("prometheus-collectd-exporter.service"); + waitForOpenPort(9103); + succeed("curl -sSfH 'Content-Type: application/json' -X POST --data \"${postData}\" localhost:9103/collectd"); + succeed("curl -sSf localhost:9103/metrics | grep -q 'collectd_testplugin_gauge{instance=\"testhost\"} 23'"); + ''; + }; + + dnsmasq = { + exporterConfig = { + enable = true; + leasesPath = "/var/lib/dnsmasq/dnsmasq.leases"; + }; + metricProvider = { + services.dnsmasq.enable = true; + }; + exporterTest = '' + waitForUnit("prometheus-dnsmasq-exporter.service"); + waitForOpenPort(9153); + succeed("curl -sSf http://localhost:9153/metrics | grep -q 'dnsmasq_leases 0'"); + ''; + }; + + dovecot = { + exporterConfig = { + enable = true; + scopes = [ "global" ]; + socketPath = "/var/run/dovecot2/old-stats"; + user = "root"; # <- don't use user root in production + }; + metricProvider = { + services.dovecot2.enable = true; + }; + exporterTest = '' + waitForUnit("prometheus-dovecot-exporter.service"); + waitForOpenPort(9166); + succeed("curl -sSf http://localhost:9166/metrics | grep -q 'dovecot_up{scope=\"global\"} 1'"); + ''; + }; + + fritzbox = { # TODO add proper test case + exporterConfig = { + enable = true; + }; + exporterTest = '' + waitForUnit("prometheus-fritzbox-exporter.service"); + waitForOpenPort(9133); + succeed("curl -sSf http://localhost:9133/metrics | grep -q 'fritzbox_exporter_collect_errors 0'"); + ''; + }; + + json = { + exporterConfig = { + enable = true; + url = "http://localhost"; + configFile = pkgs.writeText "json-exporter-conf.json" (builtins.toJSON [{ + name = "json_test_metric"; + path = "$.test"; + }]); + }; + metricProvider = { + systemd.services.prometheus-json-exporter.after = [ "nginx.service" ]; + services.nginx = { + enable = true; + virtualHosts.localhost.locations."/".extraConfig = '' + return 200 "{\"test\":1}"; + ''; + }; + }; + exporterTest = '' + waitForUnit("nginx.service"); + waitForOpenPort(80); + waitForUnit("prometheus-json-exporter.service"); + waitForOpenPort(7979); + succeed("curl -sSf localhost:7979/metrics | grep -q 'json_test_metric 1'"); + ''; + }; + + nginx = { + exporterConfig = { + enable = true; + }; + metricProvider = { + services.nginx = { + enable = true; + statusPage = true; + virtualHosts."/".extraConfig = "return 204;"; + }; + }; + exporterTest = '' + waitForUnit("nginx.service") + waitForUnit("prometheus-nginx-exporter.service") + waitForOpenPort(9113) + succeed("curl -sSf http://localhost:9113/metrics | grep -q 'nginx_up 1'") + ''; + }; + + node = { + exporterConfig = { + enable = true; + }; + exporterTest = '' + waitForUnit("prometheus-node-exporter.service"); + waitForOpenPort(9100); + succeed("curl -sSf http://localhost:9100/metrics | grep -q 'node_exporter_build_info{.\\+} 1'"); + ''; + }; + + postfix = { + exporterConfig = { + enable = true; + }; + metricProvider = { + services.postfix.enable = true; + }; + exporterTest = '' + waitForUnit("prometheus-postfix-exporter.service"); + waitForOpenPort(9154); + succeed("curl -sSf http://localhost:9154/metrics | grep -q 'postfix_smtpd_connects_total 0'"); + ''; + }; + + snmp = { + exporterConfig = { + enable = true; + configuration.default = { + version = 2; + auth.community = "public"; + }; + }; + exporterTest = '' + waitForUnit("prometheus-snmp-exporter.service"); + waitForOpenPort(9116); + succeed("curl -sSf localhost:9116/metrics | grep -q 'snmp_request_errors_total 0'"); + ''; + }; + + surfboard = { + exporterConfig = { + enable = true; + modemAddress = "localhost"; + }; + metricProvider = { + systemd.services.prometheus-surfboard-exporter.after = [ "nginx.service" ]; + services.nginx = { + enable = true; + virtualHosts.localhost.locations."/cgi-bin/status".extraConfig = '' + return 204; + ''; + }; + }; + exporterTest = '' + waitForUnit("nginx.service"); + waitForOpenPort(80); + waitForUnit("prometheus-surfboard-exporter.service"); + waitForOpenPort(9239); + succeed("curl -sSf localhost:9239/metrics | grep -q 'surfboard_up 1'"); + ''; + }; + + varnish = { + exporterConfig = { + enable = true; + instance = "/var/spool/varnish/varnish"; + group = "varnish"; + }; + metricProvider = { + systemd.services.prometheus-varnish-exporter.after = [ + "varnish.service" + ]; + services.varnish = { + enable = true; + config = '' + vcl 4.0; + backend default { + .host = "127.0.0.1"; + .port = "80"; + } + ''; + }; + }; + exporterTest = '' + waitForUnit("prometheus-varnish-exporter.service"); + waitForOpenPort(9131); + succeed("curl -sSf http://localhost:9131/metrics | grep -q 'varnish_up 1'"); + ''; + }; + }; + + nodes = lib.mapAttrs (exporter: testConfig: lib.mkMerge [{ + services.prometheus.exporters.${exporter} = testConfig.exporterConfig; + } testConfig.metricProvider or {}]) exporterTests; + + testScript = lib.concatStrings (lib.mapAttrsToList (exporter: testConfig: ('' + subtest "${exporter}", sub { + ${"$"+exporter}->start(); + ${lib.concatStringsSep " " (map (line: '' + ${"$"+exporter}->${line}; + '') (lib.splitString "\n" (lib.removeSuffix "\n" testConfig.exporterTest)))} + ${"$"+exporter}->shutdown(); + }; + '')) exporterTests); +in +{ + name = "prometheus-exporters"; + + inherit nodes testScript; + + meta = with lib.maintainers; { + maintainers = [ willibutz ]; + }; +}) diff --git a/nixos/tests/redmine.nix b/nixos/tests/redmine.nix new file mode 100644 index 000000000000..330f72854cac --- /dev/null +++ b/nixos/tests/redmine.nix @@ -0,0 +1,40 @@ +import ./make-test.nix ({ pkgs, lib, ... }: +{ + name = "redmine"; + meta.maintainers = [ lib.maintainers.aanderse ]; + + machine = + { config, pkgs, ... }: + { services.mysql.enable = true; + services.mysql.package = pkgs.mariadb; + services.mysql.ensureDatabases = [ "redmine" ]; + services.mysql.ensureUsers = [ + { name = "redmine"; + ensurePermissions = { "redmine.*" = "ALL PRIVILEGES"; }; + } + ]; + + services.redmine.enable = true; + services.redmine.database.socket = "/run/mysqld/mysqld.sock"; + services.redmine.plugins = { + redmine_env_auth = pkgs.fetchurl { + url = https://github.com/Intera/redmine_env_auth/archive/0.6.zip; + sha256 = "0yyr1yjd8gvvh832wdc8m3xfnhhxzk2pk3gm2psg5w9jdvd6skak"; + }; + }; + services.redmine.themes = { + dkuk-redmine_alex_skin = pkgs.fetchurl { + url = https://bitbucket.org/dkuk/redmine_alex_skin/get/1842ef675ef3.zip; + sha256 = "0hrin9lzyi50k4w2bd2b30vrf1i4fi1c0gyas5801wn8i7kpm9yl"; + }; + }; + }; + + testScript = '' + startAll; + + $machine->waitForUnit('redmine.service'); + $machine->waitForOpenPort('3000'); + $machine->succeed("curl --fail http://localhost:3000/"); + ''; +}) diff --git a/nixos/tests/upnp.nix b/nixos/tests/upnp.nix new file mode 100644 index 000000000000..3f2dd13fb560 --- /dev/null +++ b/nixos/tests/upnp.nix @@ -0,0 +1,94 @@ +# This tests whether UPnP port mappings can be created using Miniupnpd +# and Miniupnpc. +# It runs a Miniupnpd service on one machine, and verifies +# a client can indeed create a port mapping using Miniupnpc. If +# this succeeds an external client will try to connect to the port +# mapping. + +import ./make-test.nix ({ pkgs, ... }: + +let + internalRouterAddress = "192.168.3.1"; + internalClient1Address = "192.168.3.2"; + externalRouterAddress = "80.100.100.1"; + externalClient2Address = "80.100.100.2"; +in +{ + name = "upnp"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ bobvanderlinden ]; + }; + + nodes = + { + router = + { pkgs, nodes, ... }: + { virtualisation.vlans = [ 1 2 ]; + networking.nat.enable = true; + networking.nat.internalInterfaces = [ "eth2" ]; + networking.nat.externalInterface = "eth1"; + networking.firewall.enable = true; + networking.firewall.trustedInterfaces = [ "eth2" ]; + networking.interfaces.eth1.ipv4.addresses = [ + { address = externalRouterAddress; prefixLength = 24; } + ]; + networking.interfaces.eth2.ipv4.addresses = [ + { address = internalRouterAddress; prefixLength = 24; } + ]; + services.miniupnpd = { + enable = true; + externalInterface = "eth1"; + internalIPs = [ "eth2" ]; + appendConfig = '' + ext_ip=${externalRouterAddress} + ''; + }; + }; + + client1 = + { pkgs, nodes, ... }: + { environment.systemPackages = [ pkgs.miniupnpc pkgs.netcat ]; + virtualisation.vlans = [ 2 ]; + networking.defaultGateway = internalRouterAddress; + networking.interfaces.eth1.ipv4.addresses = [ + { address = internalClient1Address; prefixLength = 24; } + ]; + networking.firewall.enable = false; + + services.httpd.enable = true; + services.httpd.listen = [{ ip = "*"; port = 9000; }]; + services.httpd.adminAddr = "foo@example.org"; + services.httpd.documentRoot = "/tmp"; + }; + + client2 = + { pkgs, ... }: + { environment.systemPackages = [ pkgs.miniupnpc ]; + virtualisation.vlans = [ 1 ]; + networking.interfaces.eth1.ipv4.addresses = [ + { address = externalClient2Address; prefixLength = 24; } + ]; + networking.firewall.enable = false; + }; + }; + + testScript = + { nodes, ... }: + '' + startAll; + + # Wait for network and miniupnpd. + $router->waitForUnit("network-online.target"); + # $router->waitForUnit("nat"); + $router->waitForUnit("firewall.service"); + $router->waitForUnit("miniupnpd"); + + $client1->waitForUnit("network-online.target"); + + $client1->succeed("upnpc -a ${internalClient1Address} 9000 9000 TCP"); + + $client1->waitForUnit("httpd"); + $client2->waitUntilSucceeds("curl http://${externalRouterAddress}:9000/"); + ''; + +}) |