diff options
Diffstat (limited to 'nixos')
279 files changed, 8274 insertions, 3020 deletions
diff --git a/nixos/doc/manual/Makefile b/nixos/doc/manual/Makefile new file mode 100644 index 000000000000..b15fbaa270fc --- /dev/null +++ b/nixos/doc/manual/Makefile @@ -0,0 +1,8 @@ +debug: + nix-shell --packages xmloscopy \ + --run 'xmloscopy --docbook5 ./manual.xml ./manual-combined.xml' + +generated: ./options-to-docbook.xsl + nix-build ../../release.nix \ + --attr manualGeneratedSources.x86_64-linux \ + --out-link ./generated diff --git a/nixos/doc/manual/configuration/configuration.xml b/nixos/doc/manual/configuration/configuration.xml index 8677c13db40f..f092c7e207ff 100644 --- a/nixos/doc/manual/configuration/configuration.xml +++ b/nixos/doc/manual/configuration/configuration.xml @@ -25,9 +25,8 @@ effect after you run <command>nixos-rebuild</command>.</para> <xi:include href="networking.xml" /> <xi:include href="linux-kernel.xml" /> -<xi:include href="modules.xml" xpointer="xpointer(//section[@id='modules']/*)" /> +<xi:include href="../generated/modules.xml" xpointer="xpointer(//section[@id='modules']/*)" /> <!-- Apache; libvirtd virtualisation --> </part> - diff --git a/nixos/doc/manual/configuration/summary.xml b/nixos/doc/manual/configuration/summary.xml index be1f2263149e..38032c5d9dc3 100644 --- a/nixos/doc/manual/configuration/summary.xml +++ b/nixos/doc/manual/configuration/summary.xml @@ -53,7 +53,7 @@ manual</link> for the rest.</para> </row> <row> <entry><literal>{ x = 1; y = 2; }</literal></entry> - <entry>An set with attributes names <literal>x</literal> and <literal>y</literal></entry> + <entry>A set with attributes named <literal>x</literal> and <literal>y</literal></entry> </row> <row> <entry><literal>{ foo.bar = 1; }</literal></entry> diff --git a/nixos/doc/manual/default.nix b/nixos/doc/manual/default.nix index 6098b057a370..2c6309474b37 100644 --- a/nixos/doc/manual/default.nix +++ b/nixos/doc/manual/default.nix @@ -87,7 +87,7 @@ let echo "for hints about the offending path)." exit 1 fi - ${libxslt.bin}/bin/xsltproc \ + ${buildPackages.libxslt.bin}/bin/xsltproc \ --stringparam revision '${revision}' \ -o $out ${./options-to-docbook.xsl} $optionsXML ''; @@ -102,13 +102,18 @@ let </section> ''; + generatedSources = runCommand "generated-docbook" {} '' + mkdir $out + ln -s ${modulesDoc} $out/modules.xml + ln -s ${optionsDocBook} $out/options-db.xml + printf "%s" "${version}" > $out/version + ''; + copySources = '' cp -prd $sources/* . # */ + ln -s ${generatedSources} ./generated chmod -R u+w . - ln -s ${modulesDoc} configuration/modules.xml - ln -s ${optionsDocBook} options-db.xml - printf "%s" "${version}" > version ''; toc = builtins.toFile "toc.xml" @@ -124,11 +129,12 @@ let manualXsltprocOptions = toString [ "--param section.autolabel 1" "--param section.label.includes.component.label 1" - "--stringparam html.stylesheet style.css" + "--stringparam html.stylesheet 'style.css overrides.css highlightjs/mono-blue.css'" + "--stringparam html.script './highlightjs/highlight.pack.js ./highlightjs/loader.js'" "--param xref.with.number.and.title 1" "--param toc.section.depth 3" "--stringparam admon.style ''" - "--stringparam callout.graphics.extension .gif" + "--stringparam callout.graphics.extension .svg" "--stringparam current.docid manual" "--param chunk.section.depth 0" "--param chunk.first.sections 1" @@ -139,7 +145,7 @@ let manual-combined = runCommand "nixos-manual-combined" { inherit sources; - buildInputs = [ libxml2 libxslt ]; + nativeBuildInputs = [ buildPackages.libxml2.bin buildPackages.libxslt.bin ]; meta.description = "The NixOS manual as plain docbook XML"; } '' @@ -194,7 +200,7 @@ let olinkDB = runCommand "manual-olinkdb" { inherit sources; - buildInputs = [ libxml2 libxslt ]; + nativeBuildInputs = [ buildPackages.libxml2.bin buildPackages.libxslt.bin ]; } '' xsltproc \ @@ -223,6 +229,7 @@ let ''; in rec { + inherit generatedSources; # The NixOS options in JSON format. optionsJSON = runCommand "options-json" @@ -244,7 +251,7 @@ in rec { # Generate the NixOS manual. manual = runCommand "nixos-manual" { inherit sources; - buildInputs = [ libxml2 libxslt ]; + nativeBuildInputs = [ buildPackages.libxml2.bin buildPackages.libxslt.bin ]; meta.description = "The NixOS manual in HTML format"; allowedReferences = ["out"]; } @@ -260,9 +267,11 @@ in rec { ${manual-combined}/manual-combined.xml mkdir -p $dst/images/callouts - cp ${docbook5_xsl}/xml/xsl/docbook/images/callouts/*.gif $dst/images/callouts/ + cp ${docbook5_xsl}/xml/xsl/docbook/images/callouts/*.svg $dst/images/callouts/ - cp ${./style.css} $dst/style.css + cp ${../../../doc/style.css} $dst/style.css + cp ${../../../doc/overrides.css} $dst/overrides.css + cp -r ${pkgs.documentation-highlighter} $dst/highlightjs mkdir -p $out/nix-support echo "nix-build out $out" >> $out/nix-support/hydra-build-products @@ -272,7 +281,7 @@ in rec { manualEpub = runCommand "nixos-manual-epub" { inherit sources; - buildInputs = [ libxml2 libxslt zip ]; + buildInputs = [ libxml2.bin libxslt.bin zip ]; } '' # Generate the epub manual. @@ -286,7 +295,7 @@ in rec { ${manual-combined}/manual-combined.xml mkdir -p $dst/epub/OEBPS/images/callouts - cp -r ${docbook5_xsl}/xml/xsl/docbook/images/callouts/*.gif $dst/epub/OEBPS/images/callouts # */ + cp -r ${docbook5_xsl}/xml/xsl/docbook/images/callouts/*.svg $dst/epub/OEBPS/images/callouts # */ echo "application/epub+zip" > mimetype manual="$dst/nixos-manual.epub" zip -0Xq "$manual" mimetype @@ -302,7 +311,7 @@ in rec { # Generate the NixOS manpages. manpages = runCommand "nixos-manpages" { inherit sources; - buildInputs = [ libxml2 libxslt ]; + nativeBuildInputs = [ buildPackages.libxml2.bin buildPackages.libxslt.bin ]; allowedReferences = ["out"]; } '' diff --git a/nixos/doc/manual/development/importing-modules.xml b/nixos/doc/manual/development/importing-modules.xml new file mode 100644 index 000000000000..ec1da09b9507 --- /dev/null +++ b/nixos/doc/manual/development/importing-modules.xml @@ -0,0 +1,59 @@ +<section xmlns="http://docbook.org/ns/docbook" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:xi="http://www.w3.org/2001/XInclude" + version="5.0" + xml:id="sec-importing-modules"> + +<title>Importing Modules</title> + +<para> + Sometimes NixOS modules need to be used in configuration but exist + outside of Nixpkgs. These modules can be imported: +</para> + +<programlisting> +{ config, lib, pkgs, ... }: + +{ + imports = + [ # Use a locally-available module definition in + # ./example-module/default.nix + ./example-module + ]; + + services.exampleModule.enable = true; +} +</programlisting> + +<para> + The environment variable <literal>NIXOS_EXTRA_MODULE_PATH</literal> is + an absolute path to a NixOS module that is included alongside the + Nixpkgs NixOS modules. Like any NixOS module, this module can import + additional modules: +</para> + +<programlisting> +# ./module-list/default.nix +[ + ./example-module1 + ./example-module2 +] +</programlisting> + +<programlisting> +# ./extra-module/default.nix +{ imports = import ./module-list.nix; } +</programlisting> + +<programlisting> +# NIXOS_EXTRA_MODULE_PATH=/absolute/path/to/extra-module +{ config, lib, pkgs, ... }: + +{ + # No `imports` needed + + services.exampleModule1.enable = true; +} +</programlisting> + +</section> diff --git a/nixos/doc/manual/development/option-types.xml b/nixos/doc/manual/development/option-types.xml index ec940d5d2b86..13fa8d1e114c 100644 --- a/nixos/doc/manual/development/option-types.xml +++ b/nixos/doc/manual/development/option-types.xml @@ -282,8 +282,8 @@ options.mod = mkOption { option set (<xref linkend='ex-submodule-listof-definition' />).</para> -<example xml:id='ex-submodule-listof-declaration'><title>Declaration of a list - nof submodules</title> +<example xml:id='ex-submodule-listof-declaration'><title>Declaration of a list + of submodules</title> <screen> options.mod = mkOption { description = "submodule example"; diff --git a/nixos/doc/manual/development/writing-documentation.xml b/nixos/doc/manual/development/writing-documentation.xml index 59a287717acb..8b787fae1fe0 100644 --- a/nixos/doc/manual/development/writing-documentation.xml +++ b/nixos/doc/manual/development/writing-documentation.xml @@ -18,13 +18,25 @@ <para> The DocBook sources of the <xref linkend="book-nixos-manual"/> are in the <link xlink:href="https://github.com/NixOS/nixpkgs/tree/master/nixos/doc/manual"><filename>nixos/doc/manual</filename></link> - subdirectory of the Nixpkgs repository. If you make modifications to - the manual, it's important to build it before committing. You can do - that as follows: + subdirectory of the Nixpkgs repository. +</para> - <screen>nix-build nixos/release.nix -A manual.x86_64-linux</screen> +<para> + You can quickly validate your edits with <command>make</command>: </para> +<screen> + $ cd /path/to/nixpkgs/nixos/doc/manual + $ make +</screen> + +<para> + Once you are done making modifications to the manual, it's important + to build it before committing. You can do that as follows: +</para> + +<screen>nix-build nixos/release.nix -A manual.x86_64-linux</screen> + <para> When this command successfully finishes, it will tell you where the manual got generated. The HTML will be accessible through the diff --git a/nixos/doc/manual/development/writing-modules.xml b/nixos/doc/manual/development/writing-modules.xml index cb363b45675b..a49f99cb2669 100644 --- a/nixos/doc/manual/development/writing-modules.xml +++ b/nixos/doc/manual/development/writing-modules.xml @@ -180,6 +180,7 @@ in { <xi:include href="option-def.xml" /> <xi:include href="assertions.xml" /> <xi:include href="meta-attributes.xml" /> +<xi:include href="importing-modules.xml" /> <xi:include href="replace-modules.xml" /> </chapter> diff --git a/nixos/doc/manual/installation/installing-usb.xml b/nixos/doc/manual/installation/installing-usb.xml index 122a4745f194..d68cd6162632 100644 --- a/nixos/doc/manual/installation/installing-usb.xml +++ b/nixos/doc/manual/installation/installing-usb.xml @@ -51,7 +51,7 @@ ISO, copy its contents verbatim to your drive, then either: <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> + <literal>copytoram</literal> to the <literal>options</literal> field.</para> </listitem> </itemizedlist> </para> diff --git a/nixos/doc/manual/installation/installing.xml b/nixos/doc/manual/installation/installing.xml index ba6098d917d0..6b08bdb318bc 100644 --- a/nixos/doc/manual/installation/installing.xml +++ b/nixos/doc/manual/installation/installing.xml @@ -115,23 +115,17 @@ for a UEFI installation is by and large the same as a BIOS installation. The dif <varlistentry><term>UEFI systems</term> <listitem><para>For creating boot partitions: <command>mkfs.fat</command>. Again it’s recommended to assign a - label to the boot partition: <option>-L + label to the boot partition: <option>-n <replaceable>label</replaceable></option>. For example: <screen> -# mkfs.fat -F 32 -L boot /dev/sda3</screen> +# mkfs.fat -F 32 -n boot /dev/sda3</screen> </para></listitem></varlistentry></variablelist></listitem> <listitem><para>For creating LVM volumes, the LVM commands, e.g., - -<screen> -# pvcreate /dev/sda1 /dev/sdb1 -# vgcreate MyVolGroup /dev/sda1 /dev/sdb1 -# lvcreate --size 2G --name bigdisk MyVolGroup -# lvcreate --size 1G --name smalldisk MyVolGroup</screen> - - </para></listitem> + <command>pvcreate</command>, <command>vgcreate</command>, and + <command>lvcreate</command>.</para></listitem> <listitem><para>For creating software RAID devices, use <command>mdadm</command>.</para></listitem> @@ -155,6 +149,7 @@ for a UEFI installation is by and large the same as a BIOS installation. The dif <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> @@ -369,8 +364,9 @@ drive (here <filename>/dev/sda</filename>). <xref linkend="ex-config" # mkfs.ext4 -L nixos /dev/sda1 # mkswap -L swap /dev/sda2 # swapon /dev/sda2 -# mkfs.fat -F 32 -L boot /dev/sda3 # <lineannotation>(for UEFI systems only)</lineannotation> +# mkfs.fat -F 32 -n boot /dev/sda3 # <lineannotation>(for UEFI systems only)</lineannotation> # mount /dev/disk/by-label/nixos /mnt +# mkdir -p /mnt/boot # <lineannotation>(for UEFI systems only)</lineannotation> # mount /dev/disk/by-label/boot /mnt/boot # <lineannotation>(for UEFI systems only)</lineannotation> # nixos-generate-config --root /mnt # nano /mnt/etc/nixos/configuration.nix diff --git a/nixos/doc/manual/man-configuration.xml b/nixos/doc/manual/man-configuration.xml index 05531b3909a3..37ffb9d648a9 100644 --- a/nixos/doc/manual/man-configuration.xml +++ b/nixos/doc/manual/man-configuration.xml @@ -31,7 +31,8 @@ therein.</para> <para>You can use the following options in <filename>configuration.nix</filename>.</para> -<xi:include href="options-db.xml" /> +<xi:include href="./generated/options-db.xml" + xpointer="configuration-variable-list" /> </refsection> diff --git a/nixos/doc/manual/man-nixos-build-vms.xml b/nixos/doc/manual/man-nixos-build-vms.xml index 878ebee05273..f4b59a7c6d4b 100644 --- a/nixos/doc/manual/man-nixos-build-vms.xml +++ b/nixos/doc/manual/man-nixos-build-vms.xml @@ -40,7 +40,7 @@ points to the generated virtual network. test1 = {pkgs, config, ...}: { services.openssh.enable = true; - nixpkgs.system = "i686-linux"; + nixpkgs.localSystem.system = "i686-linux"; deployment.targetHost = "test1.example.net"; # Other NixOS options @@ -51,7 +51,7 @@ points to the generated virtual network. services.openssh.enable = true; services.httpd.enable = true; environment.systemPackages = [ pkgs.lynx ]; - nixpkgs.system = "x86_64-linux"; + nixpkgs.localSystem.system = "x86_64-linux"; deployment.targetHost = "test2.example.net"; # Other NixOS options @@ -66,7 +66,7 @@ In each NixOS configuration, two attributes have a special meaning. The <varname>deployment.targetHost</varname> specifies the address (domain name or IP address) of the system which is used by <command>ssh</command> to perform -remote deployment operations. The <varname>nixpkgs.system</varname> +remote deployment operations. The <varname>nixpkgs.localSystem.system</varname> attribute can be used to specify an architecture for the target machine, such as <varname>i686-linux</varname> which builds a 32-bit NixOS configuration. Omitting this property will build the configuration diff --git a/nixos/doc/manual/manual.xml b/nixos/doc/manual/manual.xml index 9aa332f026da..676924e5c8b2 100644 --- a/nixos/doc/manual/manual.xml +++ b/nixos/doc/manual/manual.xml @@ -6,7 +6,7 @@ <info> <title>NixOS Manual</title> - <subtitle>Version <xi:include href="version" parse="text" /></subtitle> + <subtitle>Version <xi:include href="./generated/version" parse="text" /></subtitle> </info> <preface> @@ -39,7 +39,8 @@ <appendix xml:id="ch-options"> <title>Configuration Options</title> - <xi:include href="options-db.xml" /> + <xi:include href="./generated/options-db.xml" + xpointer="configuration-variable-list" /> </appendix> <xi:include href="release-notes/release-notes.xml" /> diff --git a/nixos/doc/manual/options-to-docbook.xsl b/nixos/doc/manual/options-to-docbook.xsl index 7b45b233ab2a..43a69806a2b0 100644 --- a/nixos/doc/manual/options-to-docbook.xsl +++ b/nixos/doc/manual/options-to-docbook.xsl @@ -15,9 +15,9 @@ <xsl:template match="/expr/list"> - - <variablelist> - + <appendix> + <title>Configuration Options</title> + <variablelist xml:id="configuration-variable-list"> <xsl:for-each select="attrs"> <xsl:variable name="id" select="concat('opt-', str:replace(str:replace(str:replace(str:replace(attr[@name = 'name']/string/@value, '*', '_'), '<', '_'), '>', '_'), '?', '_'))" /> <varlistentry> @@ -100,7 +100,7 @@ </xsl:for-each> </variablelist> - + </appendix> </xsl:template> diff --git a/nixos/doc/manual/release-notes/rl-1803.xml b/nixos/doc/manual/release-notes/rl-1803.xml index b755245a69fb..9221c2951ed2 100644 --- a/nixos/doc/manual/release-notes/rl-1803.xml +++ b/nixos/doc/manual/release-notes/rl-1803.xml @@ -4,7 +4,7 @@ version="5.0" xml:id="sec-release-18.03"> -<title>Release 18.03 (“Impala”, 2018/03/??)</title> +<title>Release 18.03 (“Impala”, 2018/04/04)</title> <section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" @@ -18,6 +18,20 @@ has the following highlights: </para> <itemizedlist> + + <listitem> + <para> + End of support is planned for end of October 2018, handing over to 18.09. + </para> + </listitem> + + <listitem> + <para> + Platform support: x86_64-linux and x86_64-darwin since release time (the latter isn't NixOS, really). + Binaries for aarch64-linux are available, but no channel exists yet, as it's waiting for some test fixes, etc. + </para> + </listitem> + <listitem> <para> Nix now defaults to 2.0; see its @@ -27,13 +41,13 @@ has the following highlights: </para> <listitem> <para> - Linux kernel defaults to the 4.14 branch (it was 4.9). + Core version changes: linux: 4.9 -> 4.14, glibc: 2.25 -> 2.26, gcc: 6 -> 7, systemd: 234 -> 237. </para> </listitem> <listitem> <para> - GCC defaults to 7.x (it was 6.x). + Desktop version changes: gnome: 3.24 -> 3.26, (KDE) plasma-desktop: 5.10 -> 5.12. </para> </listitem> @@ -59,13 +73,7 @@ has the following highlights: </para> </listitem> <listitem> - <para> - The GNOME version is now 3.26. - </para> - </listitem> - - <listitem> - <para>PHP now defaults to PHP 7.2</para> + <para>PHP now defaults to PHP 7.2, updated from 7.1.</para> </listitem> </itemizedlist> @@ -81,9 +89,66 @@ has the following highlights: </para> <para>The following new services were added since the last release:</para> <itemizedlist> - <listitem> - <para></para> - </listitem> + <listitem><para><literal>./config/krb5/default.nix</literal></para></listitem> + <listitem><para><literal>./hardware/digitalbitbox.nix</literal></para></listitem> + <listitem><para><literal>./misc/label.nix</literal></para></listitem> + <listitem><para><literal>./programs/ccache.nix</literal></para></listitem> + <listitem><para><literal>./programs/criu.nix</literal></para></listitem> + <listitem><para><literal>./programs/digitalbitbox/default.nix</literal></para></listitem> + <listitem><para><literal>./programs/less.nix</literal></para></listitem> + <listitem><para><literal>./programs/npm.nix</literal></para></listitem> + <listitem><para><literal>./programs/plotinus.nix</literal></para></listitem> + <listitem><para><literal>./programs/rootston.nix</literal></para></listitem> + <listitem><para><literal>./programs/systemtap.nix</literal></para></listitem> + <listitem><para><literal>./programs/sway.nix</literal></para></listitem> + <listitem><para><literal>./programs/udevil.nix</literal></para></listitem> + <listitem><para><literal>./programs/way-cooler.nix</literal></para></listitem> + <listitem><para><literal>./programs/yabar.nix</literal></para></listitem> + <listitem><para><literal>./programs/zsh/zsh-autoenv.nix</literal></para></listitem> + <listitem><para><literal>./services/backup/borgbackup.nix</literal></para></listitem> + <listitem><para><literal>./services/backup/crashplan-small-business.nix</literal></para></listitem> + <listitem><para><literal>./services/desktops/dleyna-renderer.nix</literal></para></listitem> + <listitem><para><literal>./services/desktops/dleyna-server.nix</literal></para></listitem> + <listitem><para><literal>./services/desktops/pipewire.nix</literal></para></listitem> + <listitem><para><literal>./services/desktops/gnome3/chrome-gnome-shell.nix</literal></para></listitem> + <listitem><para><literal>./services/desktops/gnome3/tracker-miners.nix</literal></para></listitem> + <listitem><para><literal>./services/hardware/fwupd.nix</literal></para></listitem> + <listitem><para><literal>./services/hardware/interception-tools.nix</literal></para></listitem> + <listitem><para><literal>./services/hardware/u2f.nix</literal></para></listitem> + <listitem><para><literal>./services/hardware/usbmuxd.nix</literal></para></listitem> + <listitem><para><literal>./services/mail/clamsmtp.nix</literal></para></listitem> + <listitem><para><literal>./services/mail/dkimproxy-out.nix</literal></para></listitem> + <listitem><para><literal>./services/mail/pfix-srsd.nix</literal></para></listitem> + <listitem><para><literal>./services/misc/gitea.nix</literal></para></listitem> + <listitem><para><literal>./services/misc/home-assistant.nix</literal></para></listitem> + <listitem><para><literal>./services/misc/ihaskell.nix</literal></para></listitem> + <listitem><para><literal>./services/misc/logkeys.nix</literal></para></listitem> + <listitem><para><literal>./services/misc/novacomd.nix</literal></para></listitem> + <listitem><para><literal>./services/misc/osrm.nix</literal></para></listitem> + <listitem><para><literal>./services/misc/plexpy.nix</literal></para></listitem> + <listitem><para><literal>./services/misc/pykms.nix</literal></para></listitem> + <listitem><para><literal>./services/misc/tzupdate.nix</literal></para></listitem> + <listitem><para><literal>./services/monitoring/fusion-inventory.nix</literal></para></listitem> + <listitem><para><literal>./services/monitoring/prometheus/exporters.nix</literal></para></listitem> + <listitem><para><literal>./services/network-filesystems/beegfs.nix</literal></para></listitem> + <listitem><para><literal>./services/network-filesystems/davfs2.nix</literal></para></listitem> + <listitem><para><literal>./services/network-filesystems/openafs/client.nix</literal></para></listitem> + <listitem><para><literal>./services/network-filesystems/openafs/server.nix</literal></para></listitem> + <listitem><para><literal>./services/network-filesystems/ceph.nix</literal></para></listitem> + <listitem><para><literal>./services/networking/aria2.nix</literal></para></listitem> + <listitem><para><literal>./services/networking/monero.nix</literal></para></listitem> + <listitem><para><literal>./services/networking/nghttpx/default.nix</literal></para></listitem> + <listitem><para><literal>./services/networking/nixops-dns.nix</literal></para></listitem> + <listitem><para><literal>./services/networking/rxe.nix</literal></para></listitem> + <listitem><para><literal>./services/networking/stunnel.nix</literal></para></listitem> + <listitem><para><literal>./services/web-apps/matomo.nix</literal></para></listitem> + <listitem><para><literal>./services/web-apps/restya-board.nix</literal></para></listitem> + <listitem><para><literal>./services/web-servers/mighttpd2.nix</literal></para></listitem> + <listitem><para><literal>./services/x11/fractalart.nix</literal></para></listitem> + <listitem><para><literal>./system/boot/binfmt.nix</literal></para></listitem> + <listitem><para><literal>./system/boot/grow-partition.nix</literal></para></listitem> + <listitem><para><literal>./tasks/filesystems/ecryptfs.nix</literal></para></listitem> + <listitem><para><literal>./virtualisation/hyperv-guest.nix</literal></para></listitem> </itemizedlist> </section> @@ -174,7 +239,7 @@ following incompatible changes:</para> the <literal>openssh_with_kerberos</literal> package is now a deprecated alias. If you do not want Kerberos support, - you can do <literal>openssh.override { withKerboros = false; }</literal>. + you can do <literal>openssh.override { withKerberos = false; }</literal>. Note, this also applies to the <literal>openssh_hpn</literal> package. </para> </listitem> @@ -322,6 +387,43 @@ following incompatible changes:</para> <link xlink:href="https://github.com/rvl/pump.io-nixos">external module</link>. </para> </listitem> + <listitem> + <para> + The Prosody XMPP server has received a major update. The following modules were renamed: + <itemizedlist> + <listitem> + <para> + <option>services.prosody.modules.httpserver</option> is now <option>services.prosody.modules.http_files</option> + </para> + </listitem> + <listitem> + <para> + <option>services.prosody.modules.console</option> is now <option>services.prosody.modules.admin_telnet</option> + </para> + </listitem> + </itemizedlist> + </para> + + <para> + Many new modules are now core modules, most notably <option>services.prosody.modules.carbons</option> + and <option>services.prosody.modules.mam</option>. + </para> + + <para> + The better-performing <literal>libevent</literal> backend is now enabled by default. + </para> + + <para> + <literal>withCommunityModules</literal> now passes through the modules to <option>services.prosody.extraModules</option>. + Use <literal>withOnlyInstalledCommunityModules</literal> for modules that should not be enabled directly, e.g <literal>lib_ldap</literal>. + </para> + </listitem> + <listitem> + <para> + All prometheus exporter modules are now defined as submodules. + The exporters are configured using <literal>services.prometheus.exporters</literal>. + </para> + </listitem> </itemizedlist> </section> @@ -383,15 +485,6 @@ following incompatible changes:</para> </listitem> <listitem> <para> - The option <option>services.xserver.desktopManager.default</option> is now - <literal>none</literal> by default. An assertion failure is thrown if WM's - and DM's default are <literal>none</literal>. - To explicitly run a plain X session without and DM or WM, the newly - introduced option <option>services.xserver.plainX</option> must be set to true. - </para> - </listitem> - <listitem> - <para> The option <option>services.logstash.listenAddress</option> is now <literal>127.0.0.1</literal> by default. Previously the default behaviour was to listen on all interfaces. </para> diff --git a/nixos/doc/manual/release-notes/rl-1809.xml b/nixos/doc/manual/release-notes/rl-1809.xml index f44d9cad52f0..ce06a23beba0 100644 --- a/nixos/doc/manual/release-notes/rl-1809.xml +++ b/nixos/doc/manual/release-notes/rl-1809.xml @@ -4,7 +4,7 @@ version="5.0" xml:id="sec-release-18.09"> -<title>Release 18.09 (“??”, 2018/09/??)</title> +<title>Release 18.09 (“Jellyfish”, 2018/09/??)</title> <section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" @@ -58,6 +58,23 @@ following incompatible changes:</para> <itemizedlist> <listitem> <para> + <literal>lib.strict</literal> is removed. Use <literal>builtins.seq</literal> instead. + </para> + </listitem> + <listitem> + <para> + The <literal>clementine</literal> package points now to the free derivation. + <literal>clementineFree</literal> is removed now and <literal>clementineUnfree</literal> + points to the package which is bundled with the unfree <literal>libspotify</literal> package. + </para> + </listitem> + <listitem> + <para> + The <literal>netcat</literal> package is now taken directly from OpenBSD's + <literal>libressl</literal>, instead of relying on Debian's fork. The new + version should be very close to the old version, but there are some minor + differences. Importantly, flags like -b, -q, -C, and -Z are no longer + accepted by the nc command. </para> </listitem> </itemizedlist> @@ -74,6 +91,51 @@ following incompatible changes:</para> <itemizedlist> <listitem> <para> + <literal>lib.attrNamesToStr</literal> has been deprecated. Use + more specific concatenation (<literal>lib.concat(Map)StringsSep</literal>) + instead. + </para> + </listitem> + <listitem> + <para> + <literal>lib.addErrorContextToAttrs</literal> has been deprecated. Use + <literal>builtins.addErrorContext</literal> directly. + </para> + </listitem> + <listitem> + <para> + <literal>lib.showVal</literal> has been deprecated. Use + <literal>lib.traceSeqN</literal> instead. + </para> + </listitem> + <listitem> + <para> + <literal>lib.traceXMLVal</literal> has been deprecated. Use + <literal>lib.traceValFn builtins.toXml</literal> instead. + </para> + </listitem> + <listitem> + <para> + <literal>lib.traceXMLValMarked</literal> has been deprecated. Use + <literal>lib.traceValFn (x: str + builtins.toXML x)</literal> instead. + </para> + </listitem> + <listitem> + <para> + <literal>lib.traceValIfNot</literal> has been deprecated. Use + <literal>if/then/else</literal> and <literal>lib.traceValSeq</literal> + instead. + </para> + </listitem> + <listitem> + <para> + <literal>lib.traceCallXml</literal> has been deprecated. Please complain + if you use the function regularly. + </para> + <para> + The attribute <literal>lib.nixpkgsVersion</literal> has been deprecated in favor of + <literal>lib.version</literal>. Please refer to the discussion in + <link xlink:href="https://github.com/NixOS/nixpkgs/pull/39416#discussion_r183845745">NixOS/nixpkgs#39416</link> for further reference. </para> </listitem> </itemizedlist> diff --git a/nixos/doc/manual/style.css b/nixos/doc/manual/style.css deleted file mode 100644 index 3118b37ead1f..000000000000 --- a/nixos/doc/manual/style.css +++ /dev/null @@ -1,267 +0,0 @@ -/* Copied from http://bakefile.sourceforge.net/, which appears - licensed under the GNU GPL. */ - - -/*************************************************************************** - Basic headers and text: - ***************************************************************************/ - -body -{ - font-family: "Nimbus Sans L", sans-serif; - background: white; - margin: 2em 1em 2em 1em; -} - -h1, h2, h3, h4 -{ - color: #005aa0; -} - -h1 /* title */ -{ - font-size: 200%; -} - -h2 /* chapters, appendices, subtitle */ -{ - font-size: 180%; -} - -/* Extra space between chapters, appendices. */ -div.chapter > div.titlepage h2, div.appendix > div.titlepage h2 -{ - margin-top: 1.5em; -} - -div.section > div.titlepage h2 /* sections */ -{ - font-size: 150%; - margin-top: 1.5em; -} - -h3 /* subsections */ -{ - font-size: 125%; -} - -div.simplesect h2 -{ - font-size: 110%; -} - -div.appendix h3 -{ - font-size: 150%; - margin-top: 1.5em; -} - -div.refnamediv h2, div.refsynopsisdiv h2, div.refsection h2 /* refentry parts */ -{ - margin-top: 1.4em; - font-size: 125%; -} - -div.refsection h3 -{ - font-size: 110%; -} - - -/*************************************************************************** - Examples: - ***************************************************************************/ - -div.example -{ - border: 1px solid #b0b0b0; - padding: 6px 6px; - margin-left: 1.5em; - margin-right: 1.5em; - background: #f4f4f8; - border-radius: 0.4em; - box-shadow: 0.4em 0.4em 0.5em #e0e0e0; -} - -div.example p.title -{ - margin-top: 0em; -} - -div.example pre -{ - box-shadow: none; -} - - -/*************************************************************************** - Screen dumps: - ***************************************************************************/ - -pre.screen, pre.programlisting -{ - border: 1px solid #b0b0b0; - padding: 3px 3px; - margin-left: 1.5em; - margin-right: 1.5em; - color: #600000; - background: #f4f4f8; - font-family: monospace; - border-radius: 0.4em; - box-shadow: 0.4em 0.4em 0.5em #e0e0e0; -} - -div.example pre.programlisting -{ - border: 0px; - padding: 0 0; - margin: 0 0 0 0; -} - - -/*************************************************************************** - Notes, warnings etc: - ***************************************************************************/ - -.note, .warning -{ - border: 1px solid #b0b0b0; - padding: 3px 3px; - margin-left: 1.5em; - margin-right: 1.5em; - margin-bottom: 1em; - padding: 0.3em 0.3em 0.3em 0.3em; - background: #fffff5; - border-radius: 0.4em; - box-shadow: 0.4em 0.4em 0.5em #e0e0e0; -} - -div.note, div.warning -{ - font-style: italic; -} - -div.note h3, div.warning h3 -{ - color: red; - font-size: 100%; - padding-right: 0.5em; - display: inline; -} - -div.note p, div.warning p -{ - margin-bottom: 0em; -} - -div.note h3 + p, div.warning h3 + p -{ - display: inline; -} - -div.note h3 -{ - color: blue; - font-size: 100%; -} - -div.navfooter * -{ - font-size: 90%; -} - - -/*************************************************************************** - Links colors and highlighting: - ***************************************************************************/ - -a { text-decoration: none; } -a:hover { text-decoration: underline; } -a:link { color: #0048b3; } -a:visited { color: #002a6a; } - - -/*************************************************************************** - Table of contents: - ***************************************************************************/ - -div.toc -{ - font-size: 90%; -} - -div.toc dl -{ - margin-top: 0em; - margin-bottom: 0em; -} - - -/*************************************************************************** - Special elements: - ***************************************************************************/ - -tt, code -{ - color: #400000; -} - -.term -{ - font-weight: bold; - -} - -div.variablelist dd p, div.glosslist dd p -{ - margin-top: 0em; -} - -div.variablelist dd, div.glosslist dd -{ - margin-left: 1.5em; -} - -div.glosslist dt -{ - font-style: italic; -} - -.varname -{ - color: #400000; -} - -span.command strong -{ - font-weight: normal; - color: #400000; -} - -div.calloutlist table -{ - box-shadow: none; -} - -table -{ - border-collapse: collapse; - box-shadow: 0.4em 0.4em 0.5em #e0e0e0; -} - -table.simplelist -{ - text-align: left; - color: #005aa0; - border: 0; - padding: 5px; - background: #fffff5; - font-weight: normal; - font-style: italic; - box-shadow: none; - margin-bottom: 1em; -} - -div.navheader table, div.navfooter table { - box-shadow: none; -} diff --git a/nixos/lib/eval-config.nix b/nixos/lib/eval-config.nix index 2e7971cca810..97c79487df4c 100644 --- a/nixos/lib/eval-config.nix +++ b/nixos/lib/eval-config.nix @@ -26,7 +26,7 @@ , lib ? import ../../lib }: -let extraArgs_ = extraArgs; pkgs_ = pkgs; system_ = system; +let extraArgs_ = extraArgs; pkgs_ = pkgs; extraModules = let e = builtins.getEnv "NIXOS_EXTRA_MODULE_PATH"; in if e == "" then [] else [(import (builtins.toPath e))]; in @@ -36,7 +36,7 @@ let _file = ./eval-config.nix; key = _file; config = { - nixpkgs.system = lib.mkDefault system_; + nixpkgs.localSystem = lib.mkDefault { inherit system; }; _module.args.pkgs = lib.mkIf (pkgs_ != null) (lib.mkForce pkgs_); }; }; diff --git a/nixos/lib/make-ext4-fs.nix b/nixos/lib/make-ext4-fs.nix index 21c69ed560a3..986d80ff1b99 100644 --- a/nixos/lib/make-ext4-fs.nix +++ b/nixos/lib/make-ext4-fs.nix @@ -7,23 +7,22 @@ , volumeLabel }: +let + sdClosureInfo = pkgs.closureInfo { rootPaths = storePaths; }; +in + pkgs.stdenv.mkDerivation { name = "ext4-fs.img"; nativeBuildInputs = with pkgs; [e2fsprogs libfaketime perl]; - # For obtaining the closure of `storePaths'. - exportReferencesGraph = - map (x: [("closure-" + baseNameOf x) x]) storePaths; - buildCommand = '' # Add the closures of the top-level store objects. - storePaths=$(perl ${pkgs.pathsFromGraph} closure-*) + storePaths=$(cat ${sdClosureInfo}/store-paths) - # Also include a manifest of the closures in a format suitable - # for nix-store --load-db. - printRegistration=1 perl ${pkgs.pathsFromGraph} closure-* > nix-path-registration + # Also include a manifest of the closures in a format suitable for nix-store --load-db. + cp ${sdClosureInfo}/registration nix-path-registration # Make a crude approximation of the size of the target image. # If the script starts failing, increase the fudge factors here. diff --git a/nixos/lib/make-system-tarball.nix b/nixos/lib/make-system-tarball.nix index a2a0340a6bd3..92539235be75 100644 --- a/nixos/lib/make-system-tarball.nix +++ b/nixos/lib/make-system-tarball.nix @@ -1,4 +1,4 @@ -{ stdenv, perl, xz, pathsFromGraph +{ stdenv, perl, pixz, pathsFromGraph , # The file name of the resulting tarball fileName ? "nixos-system-${stdenv.system}" @@ -21,14 +21,20 @@ # Extra tar arguments , extraArgs ? "" + # Command used for compression +, compressCommand ? "pixz" + # Extension for the compressed tarball +, compressionExtension ? ".xz" + # extra inputs, like the compressor to use +, extraInputs ? [ pixz ] }: stdenv.mkDerivation { name = "tarball"; builder = ./make-system-tarball.sh; - buildInputs = [perl xz]; + buildInputs = [ perl ] ++ extraInputs; - inherit fileName pathsFromGraph extraArgs extraCommands; + inherit fileName pathsFromGraph extraArgs extraCommands compressCommand; # !!! should use XML. sources = map (x: x.source) contents; @@ -41,4 +47,6 @@ stdenv.mkDerivation { # For obtaining the closure of `storeContents'. exportReferencesGraph = map (x: [("closure-" + baseNameOf x.object) x.object]) storeContents; + + extension = compressionExtension; } diff --git a/nixos/lib/make-system-tarball.sh b/nixos/lib/make-system-tarball.sh index 73a009d8488a..1a52a284a257 100644 --- a/nixos/lib/make-system-tarball.sh +++ b/nixos/lib/make-system-tarball.sh @@ -1,5 +1,4 @@ source $stdenv/setup -set -x sources_=($sources) targets_=($targets) @@ -54,8 +53,8 @@ mkdir -p $out/tarball rm env-vars -tar --sort=name --mtime='@1' --owner=0 --group=0 --numeric-owner -cvJf $out/tarball/$fileName.tar.xz * $extraArgs +time tar --sort=name --mtime='@1' --owner=0 --group=0 --numeric-owner -c * $extraArgs | $compressCommand > $out/tarball/$fileName.tar${extension} mkdir -p $out/nix-support echo $system > $out/nix-support/system -echo "file system-tarball $out/tarball/$fileName.tar.xz" > $out/nix-support/hydra-build-products +echo "file system-tarball $out/tarball/$fileName.tar${extension}" > $out/nix-support/hydra-build-products diff --git a/nixos/lib/qemu-flags.nix b/nixos/lib/qemu-flags.nix index fcdcbf1b0077..e4c95ebdfb0d 100644 --- a/nixos/lib/qemu-flags.nix +++ b/nixos/lib/qemu-flags.nix @@ -9,7 +9,7 @@ ]; qemuSerialDevice = if pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64 then "ttyS0" - else if pkgs.stdenv.isArm || pkgs.stdenv.isAarch64 then "ttyAMA0" + else if pkgs.stdenv.isAarch32 || pkgs.stdenv.isAarch64 then "ttyAMA0" else throw "Unknown QEMU serial device for system '${pkgs.stdenv.system}'"; qemuBinary = qemuPkg: { diff --git a/nixos/lib/test-driver/Machine.pm b/nixos/lib/test-driver/Machine.pm index 78598b3efb4b..b18f48464cee 100644 --- a/nixos/lib/test-driver/Machine.pm +++ b/nixos/lib/test-driver/Machine.pm @@ -33,9 +33,20 @@ sub new { $startCommand = "qemu-kvm -m 384 " . "-net nic,model=virtio \$QEMU_OPTS "; - my $iface = $args->{hdaInterface} || "virtio"; - $startCommand .= "-drive file=" . Cwd::abs_path($args->{hda}) . ",if=$iface,werror=report " - if defined $args->{hda}; + + if (defined $args->{hda}) { + if ($args->{hdaInterface} eq "scsi") { + $startCommand .= "-drive id=hda,file=" + . Cwd::abs_path($args->{hda}) + . ",werror=report,if=none " + . "-device scsi-hd,drive=hda "; + } else { + $startCommand .= "-drive file=" . Cwd::abs_path($args->{hda}) + . ",if=" . $args->{hdaInterface} + . ",werror=report "; + } + } + $startCommand .= "-cdrom $args->{cdrom} " if defined $args->{cdrom}; $startCommand .= "-device piix3-usb-uhci -drive id=usbdisk,file=$args->{usb},if=none,readonly -device usb-storage,drive=usbdisk " @@ -612,7 +623,7 @@ sub waitForX { my ($self, $regexp) = @_; $self->nest("waiting for the X11 server", sub { retry sub { - my ($status, $out) = $self->execute("journalctl -b SYSLOG_IDENTIFIER=systemd | grep 'session opened'"); + my ($status, $out) = $self->execute("journalctl -b SYSLOG_IDENTIFIER=systemd | grep 'Reached target Current graphical'"); return 0 if $status != 0; ($status, $out) = $self->execute("[ -e /tmp/.X11-unix/X0 ]"); return 1 if $status == 0; diff --git a/nixos/lib/testing.nix b/nixos/lib/testing.nix index d990a5f8b6ac..57acc990a48f 100644 --- a/nixos/lib/testing.nix +++ b/nixos/lib/testing.nix @@ -111,6 +111,8 @@ in rec { ocrProg = tesseract_4.override { enableLanguages = [ "eng" ]; }; + imagemagick_tiff = imagemagick_light.override { inherit libtiff; }; + # Generate onvenience wrappers for running the test driver # interactively with the specified network, and for starting the # VMs from the command line. @@ -128,7 +130,7 @@ in rec { wrapProgram $out/bin/nixos-test-driver \ --add-flags "''${vms[*]}" \ ${lib.optionalString enableOCR - "--prefix PATH : '${ocrProg}/bin:${imagemagick}/bin'"} \ + "--prefix PATH : '${ocrProg}/bin:${imagemagick_tiff}/bin'"} \ --run "export testScript=\"\$(cat $out/test-script)\"" \ --set VLANS '${toString vlans}' ln -s ${testDriver}/bin/nixos-test-driver $out/bin/nixos-run-vms diff --git a/nixos/maintainers/option-usages.nix b/nixos/maintainers/option-usages.nix index 7be0255b35ac..371ee7d91808 100644 --- a/nixos/maintainers/option-usages.nix +++ b/nixos/maintainers/option-usages.nix @@ -15,7 +15,7 @@ # # $ nix-build ./option-usage.nix --argstr testOption service.xserver.enable -A txt -o service.xserver.enable._txt # -# otther target exists such as, `dotContent`, `dot`, and `pdf`. If you are +# Other targets exists such as `dotContent`, `dot`, and `pdf`. If you are # looking for the option usage of multiple options, you can provide a list # as argument. # @@ -35,7 +35,7 @@ # value is replaced by a `throw` statement which is caught by the `tryEval` # evaluation of each option value. # -# We then compare the result of the evluation of the original module, with +# We then compare the result of the evaluation of the original module, with # the result of the second evaluation, and consider that the new failures are # caused by our mutation of the `config` argument. # @@ -62,7 +62,7 @@ let "_module.args" # For some reasons which we yet have to investigate, some options cannot - # be replaced by a throw without cuasing a non-catchable failure. + # be replaced by a throw without causing a non-catchable failure. "networking.bonds" "networking.bridges" "networking.interfaces" diff --git a/nixos/maintainers/scripts/ec2/create-amis.sh b/nixos/maintainers/scripts/ec2/create-amis.sh index 347e6b9c6e0d..9461144fad5a 100755 --- a/nixos/maintainers/scripts/ec2/create-amis.sh +++ b/nixos/maintainers/scripts/ec2/create-amis.sh @@ -6,7 +6,7 @@ set -e set -o pipefail -version=$(nix-instantiate --eval --strict '<nixpkgs>' -A lib.nixpkgsVersion | sed s/'"'//g) +version=$(nix-instantiate --eval --strict '<nixpkgs>' -A lib.version | sed s/'"'//g) major=${version:0:5} echo "NixOS version is $version ($major)" diff --git a/nixos/maintainers/scripts/gce/create-gce.sh b/nixos/maintainers/scripts/gce/create-gce.sh index ef1801fe54be..0fd26d34d07f 100755 --- a/nixos/maintainers/scripts/gce/create-gce.sh +++ b/nixos/maintainers/scripts/gce/create-gce.sh @@ -3,7 +3,7 @@ set -euo pipefail -BUCKET_NAME="${BUCKET_NAME:-nixos-images}" +BUCKET_NAME="${BUCKET_NAME:-nixos-cloud-images}" TIMESTAMP="$(date +%Y%m%d%H%M)" export TIMESTAMP @@ -19,5 +19,5 @@ img_name=$(basename "$img_path") img_id=$(echo "$img_name" | sed 's|.raw.tar.gz$||;s|\.|-|g;s|_|-|g') if ! gsutil ls "gs://${BUCKET_NAME}/$img_name"; then gsutil cp "$img_path" "gs://${BUCKET_NAME}/$img_name" + gsutil acl ch -u AllUsers:R "gs://${BUCKET_NAME}/$img_name" fi -gcloud compute images create "$img_id" --source-uri "gs://${BUCKET_NAME}/$img_name" diff --git a/nixos/modules/config/gnu.nix b/nixos/modules/config/gnu.nix index ef48ccb7b4fe..93d130970190 100644 --- a/nixos/modules/config/gnu.nix +++ b/nixos/modules/config/gnu.nix @@ -26,11 +26,11 @@ with lib; nano zile texinfo # for the stand-alone Info reader ] - ++ stdenv.lib.optional (!stdenv.isArm) grub2; + ++ stdenv.lib.optional (!stdenv.isAarch32) grub2; # GNU GRUB, where available. - boot.loader.grub.enable = !pkgs.stdenv.isArm; + boot.loader.grub.enable = !pkgs.stdenv.isAarch32; boot.loader.grub.version = 2; # GNU lsh. diff --git a/nixos/modules/config/i18n.nix b/nixos/modules/config/i18n.nix index 46b22fc12854..6bf8c653e113 100644 --- a/nixos/modules/config/i18n.nix +++ b/nixos/modules/config/i18n.nix @@ -10,7 +10,7 @@ with lib; i18n = { glibcLocales = mkOption { type = types.path; - default = pkgs.glibcLocales.override { + default = pkgs.buildPackages.glibcLocales.override { allLocales = any (x: x == "all") config.i18n.supportedLocales; locales = config.i18n.supportedLocales; }; diff --git a/nixos/modules/config/no-x-libs.nix b/nixos/modules/config/no-x-libs.nix index d8980944adc0..a20910353f34 100644 --- a/nixos/modules/config/no-x-libs.nix +++ b/nixos/modules/config/no-x-libs.nix @@ -32,10 +32,10 @@ with lib; networkmanager-l2tp = pkgs.networkmanager-l2tp.override { withGnome = false; }; networkmanager-openconnect = pkgs.networkmanager-openconnect.override { withGnome = false; }; networkmanager-openvpn = pkgs.networkmanager-openvpn.override { withGnome = false; }; - networkmanager-pptp = pkgs.networkmanager-pptp.override { withGnome = false; }; networkmanager-vpnc = pkgs.networkmanager-vpnc.override { withGnome = false; }; networkmanager-iodine = pkgs.networkmanager-iodine.override { withGnome = false; }; pinentry = pkgs.pinentry_ncurses; + gobjectIntrospection = pkgs.gobjectIntrospection.override { x11Support = false; }; }; }; } diff --git a/nixos/modules/config/nsswitch.nix b/nixos/modules/config/nsswitch.nix index 7b36d4f1cbdf..c595c6932946 100644 --- a/nixos/modules/config/nsswitch.nix +++ b/nixos/modules/config/nsswitch.nix @@ -17,23 +17,23 @@ let resolved = canLoadExternalModules && config.services.resolved.enable; hostArray = [ "files" ] - ++ optionals mymachines [ "mymachines" ] - ++ optionals nssmdns [ "mdns_minimal [NOTFOUND=return]" ] - ++ optionals nsswins [ "wins" ] - ++ optionals resolved ["resolve [!UNAVAIL=return]"] + ++ optional mymachines "mymachines" + ++ optional nssmdns "mdns_minimal [NOTFOUND=return]" + ++ optional nsswins "wins" + ++ optional resolved "resolve [!UNAVAIL=return]" ++ [ "dns" ] - ++ optionals nssmdns [ "mdns" ] - ++ optionals myhostname ["myhostname" ]; + ++ optional nssmdns "mdns" + ++ optional myhostname "myhostname"; passwdArray = [ "files" ] ++ optional sssd "sss" - ++ optionals ldap [ "ldap" ] - ++ optionals mymachines [ "mymachines" ] + ++ optional ldap "ldap" + ++ optional mymachines "mymachines" ++ [ "systemd" ]; shadowArray = [ "files" ] ++ optional sssd "sss" - ++ optionals ldap [ "ldap" ]; + ++ optional ldap "ldap"; servicesArray = [ "files" ] ++ optional sssd "sss"; diff --git a/nixos/modules/config/pulseaudio.nix b/nixos/modules/config/pulseaudio.nix index a9c5fc75660d..90cea47b70ae 100644 --- a/nixos/modules/config/pulseaudio.nix +++ b/nixos/modules/config/pulseaudio.nix @@ -214,6 +214,8 @@ in { (mkIf cfg.enable { environment.systemPackages = [ overriddenPackage ]; + sound.enable = true; + environment.etc = [ { target = "asound.conf"; source = alsaConf; } diff --git a/nixos/modules/config/system-path.nix b/nixos/modules/config/system-path.nix index d3212d931605..361151665018 100644 --- a/nixos/modules/config/system-path.nix +++ b/nixos/modules/config/system-path.nix @@ -109,7 +109,6 @@ in "/sbin" "/share/applications" "/share/desktop-directories" - "/share/doc" "/share/emacs" "/share/icons" "/share/menus" diff --git a/nixos/modules/config/users-groups.nix b/nixos/modules/config/users-groups.nix index 11e969b760e0..621ca36fb6b8 100644 --- a/nixos/modules/config/users-groups.nix +++ b/nixos/modules/config/users-groups.nix @@ -35,6 +35,7 @@ let name = mkOption { type = types.str; + apply = x: assert (builtins.stringLength x < 32 || abort "Username '${x}' is longer than 31 characters which is not allowed!"); x; description = '' The name of the user account. If undefined, the name of the attribute set will be used. @@ -91,6 +92,7 @@ let group = mkOption { type = types.str; + apply = x: assert (builtins.stringLength x < 32 || abort "Group name '${x}' is longer than 31 characters which is not allowed!"); x; default = "nogroup"; description = "The user's primary group."; }; @@ -502,9 +504,6 @@ in { }; }; - # Install all the user shells - environment.systemPackages = systemShells; - users.groups = { root.gid = ids.gids.root; wheel.gid = ids.gids.wheel; @@ -541,14 +540,29 @@ in { # for backwards compatibility system.activationScripts.groups = stringAfter [ "users" ] ""; - environment.etc."subuid" = { - text = subuidFile; - mode = "0644"; - }; - environment.etc."subgid" = { - text = subgidFile; - mode = "0644"; - }; + # Install all the user shells + environment.systemPackages = systemShells; + + environment.etc = { + "subuid" = { + text = subuidFile; + mode = "0644"; + }; + "subgid" = { + text = subgidFile; + mode = "0644"; + }; + } // (mapAttrs' (name: { packages, ... }: { + name = "profiles/per-user/${name}"; + value.source = pkgs.buildEnv { + name = "user-environment"; + paths = packages; + inherit (config.environment) pathsToLink extraOutputsToInstall; + inherit (config.system.path) ignoreCollisions postBuild; + }; + }) (filterAttrs (_: u: u.packages != []) cfg.users)); + + environment.profiles = [ "/etc/profiles/per-user/$USER" ]; assertions = [ { assertion = !cfg.enforceIdUniqueness || (uidsAreUnique && gidsAreUnique); @@ -579,22 +593,4 @@ in { }; - imports = - [ (mkAliasOptionModule [ "users" "extraUsers" ] [ "users" "users" ]) - (mkAliasOptionModule [ "users" "extraGroups" ] [ "users" "groups" ]) - { - environment = { - etc = mapAttrs' (name: { packages, ... }: { - name = "profiles/per-user/${name}"; - value.source = pkgs.buildEnv { - name = "user-environment"; - paths = packages; - inherit (config.environment) pathsToLink extraOutputsToInstall; - inherit (config.system.path) ignoreCollisions postBuild; - }; - }) (filterAttrs (_: { packages, ... }: packages != []) cfg.users); - profiles = ["/etc/profiles/per-user/$USER"]; - }; - } - ]; } diff --git a/nixos/modules/config/zram.nix b/nixos/modules/config/zram.nix index ad41ad4f3d7c..c1748812821e 100644 --- a/nixos/modules/config/zram.nix +++ b/nixos/modules/config/zram.nix @@ -25,16 +25,16 @@ in type = types.bool; description = '' Enable in-memory compressed swap space provided by the zram kernel - module. It is recommended to enable only for kernel 3.14 or higher. + module. + See https://www.kernel.org/doc/Documentation/blockdev/zram.txt ''; }; numDevices = mkOption { - default = 4; + default = 1; type = types.int; description = '' - Number of zram swap devices to create. It should be equal to the - number of CPU cores your system has. + Number of zram swap devices to create. ''; }; @@ -93,7 +93,7 @@ in serviceConfig = { Type = "oneshot"; RemainAfterExit = true; - ExecStop = "${pkgs.stdenv.shell} -c 'echo 1 > /sys/class/block/${dev}/reset'"; + ExecStop = "${pkgs.runtimeShell} -c 'echo 1 > /sys/class/block/${dev}/reset'"; }; script = '' set -u diff --git a/nixos/modules/hardware/onlykey.nix b/nixos/modules/hardware/onlykey.nix new file mode 100644 index 000000000000..b6820fe01911 --- /dev/null +++ b/nixos/modules/hardware/onlykey.nix @@ -0,0 +1,33 @@ +{ config, lib, ... }: + +with lib; + +{ + + ####### interface + + options = { + + hardware.onlykey = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Enable OnlyKey device (https://crp.to/p/) support. + ''; + }; + }; + + }; + + ## As per OnlyKey's documentation piece (hhttps://docs.google.com/document/d/1Go_Rs218fKUx-j_JKhddbSVTqY6P0vQO831t2MKCJC8), + ## it is important to add udev rule for OnlyKey for it to work on Linux + + ####### implementation + + config = mkIf config.hardware.onlykey.enable { + services.udev.extraRules = builtin.readFile ./onlykey.udev; + }; + + +} diff --git a/nixos/modules/hardware/onlykey.udev b/nixos/modules/hardware/onlykey.udev new file mode 100644 index 000000000000..6583530e5684 --- /dev/null +++ b/nixos/modules/hardware/onlykey.udev @@ -0,0 +1,4 @@ +ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789B]?", ENV{ID_MM_DEVICE_IGNORE}="1" +ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789A]?", ENV{MTP_NO_PROBE}="1" +SUBSYSTEMS=="usb", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789ABCD]?", GROUP+="plugdev" +KERNEL=="ttyACM*", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789B]?", GROUP+="plugdev" diff --git a/nixos/modules/hardware/opengl.nix b/nixos/modules/hardware/opengl.nix index d9646704e6f6..b371af353cf9 100644 --- a/nixos/modules/hardware/opengl.nix +++ b/nixos/modules/hardware/opengl.nix @@ -14,7 +14,6 @@ let name = "mesa-drivers+txc-${p.mesa_drivers.version}"; paths = [ p.mesa_drivers - p.mesa_drivers.out # mainly for libGL (if cfg.s3tcSupport then p.libtxc_dxtn else p.libtxc_dxtn_s2tc) ]; }; @@ -33,89 +32,92 @@ in { options = { - hardware.opengl.enable = mkOption { - description = '' - Whether to enable OpenGL drivers. This is needed to enable - OpenGL support in X11 systems, as well as for Wayland compositors - like sway, way-cooler and Weston. It is enabled by default - by the corresponding modules, so you do not usually have to - set it yourself, only if there is no module for your wayland - compositor of choice. See services.xserver.enable, - programs.sway.enable, and programs.way-cooler.enable. - ''; - type = types.bool; - default = false; - }; - - hardware.opengl.driSupport = mkOption { - type = types.bool; - default = true; - description = '' - Whether to enable accelerated OpenGL rendering through the - Direct Rendering Interface (DRI). - ''; - }; - - hardware.opengl.driSupport32Bit = mkOption { - type = types.bool; - default = false; - description = '' - On 64-bit systems, whether to support Direct Rendering for - 32-bit applications (such as Wine). This is currently only - supported for the <literal>nvidia</literal> and - <literal>ati_unfree</literal> drivers, as well as - <literal>Mesa</literal>. - ''; - }; - - hardware.opengl.s3tcSupport = mkOption { - type = types.bool; - default = false; - description = '' - Make S3TC(S3 Texture Compression) via libtxc_dxtn available - to OpenGL drivers instead of the patent-free S2TC replacement. - - Using this library may require a patent license depending on your location. - ''; - }; - - hardware.opengl.package = mkOption { - type = types.package; - internal = true; - description = '' - The package that provides the OpenGL implementation. - ''; - }; - hardware.opengl.package32 = mkOption { - type = types.package; - internal = true; - description = '' - The package that provides the 32-bit OpenGL implementation on - 64-bit systems. Used when <option>driSupport32Bit</option> is - set. - ''; - }; - - hardware.opengl.extraPackages = mkOption { - type = types.listOf types.package; - default = []; - example = literalExample "with pkgs; [ vaapiIntel libvdpau-va-gl vaapiVdpau intel-ocl ]"; - description = '' - Additional packages to add to OpenGL drivers. This can be used - to add OpenCL drivers, VA-API/VDPAU drivers etc. - ''; - }; - - hardware.opengl.extraPackages32 = mkOption { - type = types.listOf types.package; - default = []; - example = literalExample "with pkgs.pkgsi686Linux; [ vaapiIntel libvdpau-va-gl vaapiVdpau ]"; - description = '' - Additional packages to add to 32-bit OpenGL drivers on - 64-bit systems. Used when <option>driSupport32Bit</option> is - set. This can be used to add OpenCL drivers, VA-API/VDPAU drivers etc. - ''; + hardware.opengl = { + enable = mkOption { + description = '' + Whether to enable OpenGL drivers. This is needed to enable + OpenGL support in X11 systems, as well as for Wayland compositors + like sway, way-cooler and Weston. It is enabled by default + by the corresponding modules, so you do not usually have to + set it yourself, only if there is no module for your wayland + compositor of choice. See services.xserver.enable, + programs.sway.enable, and programs.way-cooler.enable. + ''; + type = types.bool; + default = false; + }; + + driSupport = mkOption { + type = types.bool; + default = true; + description = '' + Whether to enable accelerated OpenGL rendering through the + Direct Rendering Interface (DRI). + ''; + }; + + driSupport32Bit = mkOption { + type = types.bool; + default = false; + description = '' + On 64-bit systems, whether to support Direct Rendering for + 32-bit applications (such as Wine). This is currently only + supported for the <literal>nvidia</literal> and + <literal>ati_unfree</literal> drivers, as well as + <literal>Mesa</literal>. + ''; + }; + + s3tcSupport = mkOption { + type = types.bool; + default = false; + description = '' + Make S3TC(S3 Texture Compression) via libtxc_dxtn available + to OpenGL drivers instead of the patent-free S2TC replacement. + + Using this library may require a patent license depending on your location. + ''; + }; + + package = mkOption { + type = types.package; + internal = true; + description = '' + The package that provides the OpenGL implementation. + ''; + }; + + package32 = mkOption { + type = types.package; + internal = true; + description = '' + The package that provides the 32-bit OpenGL implementation on + 64-bit systems. Used when <option>driSupport32Bit</option> is + set. + ''; + }; + + extraPackages = mkOption { + type = types.listOf types.package; + default = []; + example = literalExample "with pkgs; [ vaapiIntel libvdpau-va-gl vaapiVdpau intel-ocl ]"; + description = '' + Additional packages to add to OpenGL drivers. This can be used + to add OpenCL drivers, VA-API/VDPAU drivers etc. + ''; + }; + + extraPackages32 = mkOption { + type = types.listOf types.package; + default = []; + example = literalExample "with pkgs.pkgsi686Linux; [ vaapiIntel libvdpau-va-gl vaapiVdpau ]"; + description = '' + Additional packages to add to 32-bit OpenGL drivers on + 64-bit systems. Used when <option>driSupport32Bit</option> is + set. This can be used to add OpenCL drivers, VA-API/VDPAU drivers etc. + ''; + }; }; }; diff --git a/nixos/modules/hardware/video/amdgpu-pro.nix b/nixos/modules/hardware/video/amdgpu-pro.nix index 5cc96d8bd074..50af022b93c8 100644 --- a/nixos/modules/hardware/video/amdgpu-pro.nix +++ b/nixos/modules/hardware/video/amdgpu-pro.nix @@ -15,13 +15,19 @@ let opengl = config.hardware.opengl; + kernel = pkgs.linux_4_9.override { + extraConfig = '' + KALLSYMS_ALL y + ''; + }; + in { config = mkIf enabled { - nixpkgs.config.xorg.abiCompat = "1.18"; + nixpkgs.config.xorg.abiCompat = "1.19"; services.xserver.drivers = singleton { name = "amdgpu"; modules = [ package ]; libPath = [ package ]; }; @@ -31,6 +37,9 @@ in boot.extraModulePackages = [ package ]; + boot.kernelPackages = + pkgs.recurseIntoAttrs (pkgs.linuxPackagesFor kernel); + boot.blacklistedKernelModules = [ "radeon" ]; hardware.firmware = [ package ]; @@ -38,10 +47,15 @@ in system.activationScripts.setup-amdgpu-pro = '' mkdir -p /run/lib ln -sfn ${package}/lib ${package.libCompatDir} + ln -sfn ${package} /run/amdgpu-pro '' + optionalString opengl.driSupport32Bit '' ln -sfn ${package32}/lib ${package32.libCompatDir} ''; + system.requiredKernelConfig = with config.lib.kernelConfig; [ + (isYes "KALLSYMS_ALL") + ]; + environment.etc = { "amd/amdrc".source = package + "/etc/amd/amdrc"; "amd/amdapfxx.blb".source = package + "/etc/amd/amdapfxx.blb"; diff --git a/nixos/modules/hardware/video/nvidia.nix b/nixos/modules/hardware/video/nvidia.nix index 50c085dd7ee2..eb1952280331 100644 --- a/nixos/modules/hardware/video/nvidia.nix +++ b/nixos/modules/hardware/video/nvidia.nix @@ -16,8 +16,6 @@ let kernelPackages.nvidia_x11 else if elem "nvidiaBeta" drivers then kernelPackages.nvidia_x11_beta - else if elem "nvidiaLegacy173" drivers then - kernelPackages.nvidia_x11_legacy173 else if elem "nvidiaLegacy304" drivers then kernelPackages.nvidia_x11_legacy304 else if elem "nvidiaLegacy340" drivers then @@ -27,13 +25,6 @@ let nvidia_x11 = nvidiaForKernel config.boot.kernelPackages; nvidia_libs32 = (nvidiaForKernel pkgs_i686.linuxPackages).override { libsOnly = true; kernel = null; }; - nvidiaPackage = nvidia: pkgs: - if !nvidia.useGLVND then nvidia.out - else pkgs.buildEnv { - name = "nvidia-libs"; - paths = [ pkgs.libglvnd nvidia.out ]; - }; - enabled = nvidia_x11 != null; in @@ -59,8 +50,8 @@ in source = "${nvidia_x11.bin}/share/nvidia/nvidia-application-profiles-rc"; }; - hardware.opengl.package = nvidiaPackage nvidia_x11 pkgs; - hardware.opengl.package32 = nvidiaPackage nvidia_libs32 pkgs_i686; + hardware.opengl.package = nvidia_x11.out; + hardware.opengl.package32 = nvidia_libs32.out; environment.systemPackages = [ nvidia_x11.bin nvidia_x11.settings ] ++ lib.filter (p: p != null) [ nvidia_x11.persistenced ]; @@ -75,10 +66,10 @@ in # Create /dev/nvidia-uvm when the nvidia-uvm module is loaded. services.udev.extraRules = '' - KERNEL=="nvidia", RUN+="${pkgs.stdenv.shell} -c 'mknod -m 666 /dev/nvidiactl c $(grep nvidia-frontend /proc/devices | cut -d \ -f 1) 255'" - KERNEL=="nvidia_modeset", RUN+="${pkgs.stdenv.shell} -c 'mknod -m 666 /dev/nvidia-modeset c $(grep nvidia-frontend /proc/devices | cut -d \ -f 1) 254'" - KERNEL=="card*", SUBSYSTEM=="drm", DRIVERS=="nvidia", RUN+="${pkgs.stdenv.shell} -c 'mknod -m 666 /dev/nvidia%n c $(grep nvidia-frontend /proc/devices | cut -d \ -f 1) %n'" - KERNEL=="nvidia_uvm", RUN+="${pkgs.stdenv.shell} -c 'mknod -m 666 /dev/nvidia-uvm c $(grep nvidia-uvm /proc/devices | cut -d \ -f 1) 0'" + KERNEL=="nvidia", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidiactl c $(grep nvidia-frontend /proc/devices | cut -d \ -f 1) 255'" + KERNEL=="nvidia_modeset", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidia-modeset c $(grep nvidia-frontend /proc/devices | cut -d \ -f 1) 254'" + KERNEL=="card*", SUBSYSTEM=="drm", DRIVERS=="nvidia", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidia%n c $(grep nvidia-frontend /proc/devices | cut -d \ -f 1) %n'" + KERNEL=="nvidia_uvm", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidia-uvm c $(grep nvidia-uvm /proc/devices | cut -d \ -f 1) 0'" ''; boot.blacklistedKernelModules = [ "nouveau" "nvidiafb" ]; diff --git a/nixos/modules/installer/cd-dvd/channel.nix b/nixos/modules/installer/cd-dvd/channel.nix index 4a1983167957..01cfe8a02e10 100644 --- a/nixos/modules/installer/cd-dvd/channel.nix +++ b/nixos/modules/installer/cd-dvd/channel.nix @@ -21,7 +21,9 @@ let if [ ! -e $out/nixos/nixpkgs ]; then ln -s . $out/nixos/nixpkgs fi + echo -n ${config.system.nixos.revision} > $out/nixos/.git-revision echo -n ${config.system.nixos.versionSuffix} > $out/nixos/.version-suffix + echo ${config.system.nixos.versionSuffix} | sed -e s/pre// > $out/nixos/svn-revision ''; in diff --git a/nixos/modules/installer/cd-dvd/iso-image.nix b/nixos/modules/installer/cd-dvd/iso-image.nix index e7cbf415a223..08923970cd38 100644 --- a/nixos/modules/installer/cd-dvd/iso-image.nix +++ b/nixos/modules/installer/cd-dvd/iso-image.nix @@ -59,7 +59,7 @@ let INITRD /boot/${config.system.boot.loader.initrdFile} # A variant to boot with verbose logging to the console - LABEL boot-nomodeset + LABEL boot-debug MENU LABEL NixOS ${config.system.nixos.label}${config.isoImage.appendToMenuLabel} (debug) LINUX /boot/${config.system.boot.loader.kernelFile} APPEND init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams} loglevel=7 @@ -73,7 +73,8 @@ let APPEND ${toString config.boot.loader.grub.memtest86.params} ''; - isolinuxCfg = baseIsolinuxCfg + (optionalString config.boot.loader.grub.memtest86.enable isolinuxMemtest86Entry); + isolinuxCfg = concatStringsSep "\n" + ([ baseIsolinuxCfg ] ++ optional config.boot.loader.grub.memtest86.enable isolinuxMemtest86Entry); # The EFI boot image. efiDir = pkgs.runCommand "efi-directory" {} '' diff --git a/nixos/modules/installer/cd-dvd/sd-image-aarch64.nix b/nixos/modules/installer/cd-dvd/sd-image-aarch64.nix index 3306846b7fa7..ddf91a5656c7 100644 --- a/nixos/modules/installer/cd-dvd/sd-image-aarch64.nix +++ b/nixos/modules/installer/cd-dvd/sd-image-aarch64.nix @@ -21,9 +21,6 @@ in "it cannot be cross compiled"; }; - # Needed by RPi firmware - nixpkgs.config.allowUnfree = true; - boot.loader.grub.enable = false; boot.loader.generic-extlinux-compatible.enable = true; diff --git a/nixos/modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix b/nixos/modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix index 08903ba397a1..891923234dda 100644 --- a/nixos/modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix +++ b/nixos/modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix @@ -21,9 +21,6 @@ in "it cannot be cross compiled"; }; - # Needed by RPi firmware - nixpkgs.config.allowUnfree = true; - boot.loader.grub.enable = false; boot.loader.generic-extlinux-compatible.enable = true; diff --git a/nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix b/nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix index 2833b75b84d8..212013b5e289 100644 --- a/nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix +++ b/nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix @@ -21,9 +21,6 @@ in "it cannot be cross compiled"; }; - # Needed by RPi firmware - nixpkgs.config.allowUnfree = true; - boot.loader.grub.enable = false; boot.loader.generic-extlinux-compatible.enable = true; diff --git a/nixos/modules/installer/cd-dvd/sd-image.nix b/nixos/modules/installer/cd-dvd/sd-image.nix index 23312c073d56..c091923de60f 100644 --- a/nixos/modules/installer/cd-dvd/sd-image.nix +++ b/nixos/modules/installer/cd-dvd/sd-image.nix @@ -20,6 +20,20 @@ let in { options.sdImage = { + imageName = mkOption { + default = "${config.sdImage.imageBaseName}-${config.system.nixos.label}-${pkgs.stdenv.system}.img"; + description = '' + Name of the generated image file. + ''; + }; + + imageBaseName = mkOption { + default = "nixos-sd-image"; + description = '' + Prefix of the name of the generated image file. + ''; + }; + storePaths = mkOption { type = with types; listOf package; example = literalExample "[ pkgs.stdenv ]"; @@ -61,19 +75,25 @@ in sdImage.storePaths = [ config.system.build.toplevel ]; system.build.sdImage = pkgs.stdenv.mkDerivation { - name = "sd-image-${pkgs.stdenv.system}.img"; + name = config.sdImage.imageName; buildInputs = with pkgs; [ dosfstools e2fsprogs mtools libfaketime utillinux ]; buildCommand = '' + mkdir -p $out/nix-support $out/sd-image + export img=$out/sd-image/${config.sdImage.imageName} + + echo "${pkgs.stdenv.system}" > $out/nix-support/system + echo "file sd-image $img" >> $out/nix-support/hydra-build-products + # Create the image file sized to fit /boot and /, plus 20M of slack rootSizeBlocks=$(du -B 512 --apparent-size ${rootfsImage} | awk '{ print $1 }') bootSizeBlocks=$((${toString config.sdImage.bootSize} * 1024 * 1024 / 512)) imageSize=$((rootSizeBlocks * 512 + bootSizeBlocks * 512 + 20 * 1024 * 1024)) - truncate -s $imageSize $out + truncate -s $imageSize $img # type=b is 'W95 FAT32', type=83 is 'Linux'. - sfdisk $out <<EOF + sfdisk $img <<EOF label: dos label-id: 0x2178694e @@ -82,11 +102,11 @@ in EOF # Copy the rootfs into the SD image - eval $(partx $out -o START,SECTORS --nr 2 --pairs) - dd conv=notrunc if=${rootfsImage} of=$out seek=$START count=$SECTORS + eval $(partx $img -o START,SECTORS --nr 2 --pairs) + dd conv=notrunc if=${rootfsImage} of=$img seek=$START count=$SECTORS # Create a FAT32 /boot partition of suitable size into bootpart.img - eval $(partx $out -o START,SECTORS --nr 1 --pairs) + eval $(partx $img -o START,SECTORS --nr 1 --pairs) truncate -s $((SECTORS * 512)) bootpart.img faketime "1970-01-01 00:00:00" mkfs.vfat -i 0x2178694e -n NIXOS_BOOT bootpart.img @@ -96,7 +116,7 @@ in # Copy the populated /boot into the SD image (cd boot; mcopy -bpsvm -i ../bootpart.img ./* ::) - dd conv=notrunc if=bootpart.img of=$out seek=$START count=$SECTORS + dd conv=notrunc if=bootpart.img of=$img seek=$START count=$SECTORS ''; }; diff --git a/nixos/modules/installer/cd-dvd/system-tarball-fuloong2f.nix b/nixos/modules/installer/cd-dvd/system-tarball-fuloong2f.nix index ba84cd51098f..6d4ba96dba0c 100644 --- a/nixos/modules/installer/cd-dvd/system-tarball-fuloong2f.nix +++ b/nixos/modules/installer/cd-dvd/system-tarball-fuloong2f.nix @@ -54,7 +54,7 @@ in environment.systemPackages = [ pkgs.w3m # needed for the manual anyway pkgs.testdisk # useful for repairing boot problems - pkgs.mssys # for writing Microsoft boot sectors / MBRs + pkgs.ms-sys # for writing Microsoft boot sectors / MBRs pkgs.parted pkgs.ddrescue pkgs.ccrypt diff --git a/nixos/modules/installer/tools/nix-fallback-paths.nix b/nixos/modules/installer/tools/nix-fallback-paths.nix index 4774cf39c030..6bb556a0123c 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/6p2gambjac7xdkd2a7w1dsxdk1q5cq4d-nix-2.0"; - i686-linux = "/nix/store/zznnaijjk3nwx0cmpczxsvngmqzhl7r4-nix-2.0"; - aarch64-linux = "/nix/store/ci96w9kxfkmlc7x2vwqiz4da0r6abxnq-nix-2.0"; - x86_64-darwin = "/nix/store/xmi4fylvx4qc79ji9v5q3zfy9vfdy4sv-nix-2.0"; + x86_64-linux = "/nix/store/2gk7rk2sx2dkmsjr59gignrfdmya8f6s-nix-2.0.1"; + i686-linux = "/nix/store/5160glkphiv13qggnivyidg8r0491pbl-nix-2.0.1"; + aarch64-linux = "/nix/store/jk29zz3ns9vdkkclcyzzkpzp8dhv1x3i-nix-2.0.1"; + x86_64-darwin = "/nix/store/4a9czmrpd4hf3r80zcmga2c2lm3hbbvv-nix-2.0.1"; } diff --git a/nixos/modules/installer/tools/nixos-enter.sh b/nixos/modules/installer/tools/nixos-enter.sh index 679391189612..518dbbbf21e3 100644 --- a/nixos/modules/installer/tools/nixos-enter.sh +++ b/nixos/modules/installer/tools/nixos-enter.sh @@ -51,8 +51,9 @@ if [[ ! -e $mountPoint/etc/NIXOS ]]; then exit 126 fi -mkdir -m 0755 -p "$mountPoint/dev" +mkdir -m 0755 -p "$mountPoint/dev" "$mountPoint/sys" mount --rbind /dev "$mountPoint/dev" +mount --rbind /sys "$mountPoint/sys" # Run the activation script. Set $LOCALE_ARCHIVE to supress some Perl locale warnings. LOCALE_ARCHIVE=$system/sw/lib/locale/locale-archive chroot "$mountPoint" "$system/activate" >&2 || true diff --git a/nixos/modules/installer/tools/nixos-generate-config.pl b/nixos/modules/installer/tools/nixos-generate-config.pl index 0e0744a52e42..74b61a64667e 100644 --- a/nixos/modules/installer/tools/nixos-generate-config.pl +++ b/nixos/modules/installer/tools/nixos-generate-config.pl @@ -577,15 +577,14 @@ $bootLoaderConfig # Set your time zone. # time.timeZone = "Europe/Amsterdam"; - # List packages installed in system profile. To search by name, run: - # \$ nix-env -qaP | grep wget + # List packages installed in system profile. To search, run: + # \$ nix search wget # environment.systemPackages = with pkgs; [ # wget vim # ]; # Some programs need SUID wrappers, can be configured further or are # started in user sessions. - # programs.bash.enableCompletion = true; # programs.mtr.enable = true; # programs.gnupg.agent = { enable = true; enableSSHSupport = true; }; diff --git a/nixos/modules/installer/tools/nixos-rebuild.sh b/nixos/modules/installer/tools/nixos-rebuild.sh index 9ede74a54cd7..2af73519bc52 100644 --- a/nixos/modules/installer/tools/nixos-rebuild.sh +++ b/nixos/modules/installer/tools/nixos-rebuild.sh @@ -382,6 +382,6 @@ fi if [ "$action" = build-vm ]; then cat >&2 <<EOF -Done. The virtual machine can be started by running $(echo $pathToConfig/bin/run-*-vm). +Done. The virtual machine can be started by running $(echo $pathToConfig/bin/run-*-vm) EOF fi diff --git a/nixos/modules/installer/virtualbox-demo.nix b/nixos/modules/installer/virtualbox-demo.nix index 5316cfce906b..13a0d7f4f6ee 100644 --- a/nixos/modules/installer/virtualbox-demo.nix +++ b/nixos/modules/installer/virtualbox-demo.nix @@ -19,4 +19,6 @@ with lib; # Add some more video drivers to give X11 a shot at working in # VMware and QEMU. services.xserver.videoDrivers = mkOverride 40 [ "virtualbox" "vmware" "cirrus" "vesa" "modesetting" ]; + + powerManagement.enable = false; } diff --git a/nixos/modules/misc/documentation.nix b/nixos/modules/misc/documentation.nix new file mode 100644 index 000000000000..2e426c017080 --- /dev/null +++ b/nixos/modules/misc/documentation.nix @@ -0,0 +1,96 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let cfg = config.documentation; in + +{ + + options = { + + documentation = { + + enable = mkOption { + type = types.bool; + default = true; + description = '' + Whether to install documentation of packages from + <option>environment.systemPackages</option> into the generated system path. + + See "Multiple-output packages" chapter in the nixpkgs manual for more info. + ''; + # which is at ../../../doc/multiple-output.xml + }; + + man.enable = mkOption { + type = types.bool; + default = true; + description = '' + Whether to install manual pages and the <command>man</command> command. + This also includes "man" outputs. + ''; + }; + + info.enable = mkOption { + type = types.bool; + default = true; + description = '' + Whether to install info pages and the <command>info</command> command. + This also includes "info" outputs. + ''; + }; + + doc.enable = mkOption { + type = types.bool; + default = true; + description = '' + Whether to install documentation distributed in packages' <literal>/share/doc</literal>. + Usually plain text and/or HTML. + This also includes "doc" outputs. + ''; + }; + + dev.enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to install documentation targeted at developers. + <itemizedlist> + <listitem><para>This includes man pages targeted at developers if <option>man.enable</option> is + set (this also includes "devman" outputs).</para></listitem> + <listitem><para>This includes info pages targeted at developers if <option>info.enable</option> + is set (this also includes "devinfo" outputs).</para></listitem> + <listitem><para>This includes other pages targeted at developers if <option>doc.enable</option> + is set (this also includes "devdoc" outputs).</para></listitem> + </itemizedlist> + ''; + }; + + }; + + }; + + config = mkIf cfg.enable (mkMerge [ + + (mkIf cfg.man.enable { + environment.systemPackages = [ pkgs.man-db ]; + environment.pathsToLink = [ "/share/man" ]; + environment.extraOutputsToInstall = [ "man" ] ++ optional cfg.dev.enable [ "devman" ]; + }) + + (mkIf cfg.info.enable { + environment.systemPackages = [ pkgs.texinfoInteractive ]; + environment.pathsToLink = [ "/share/info" ]; + environment.extraOutputsToInstall = [ "info" ] ++ optional cfg.dev.enable [ "devinfo" ]; + }) + + (mkIf cfg.doc.enable { + # TODO(@oxij): put it here and remove from profiles? + # environment.systemPackages = [ pkgs.w3m ]; # w3m-nox? + environment.pathsToLink = [ "/share/doc" ]; + environment.extraOutputsToInstall = [ "doc" ] ++ optional cfg.dev.enable [ "devdoc" ]; + }) + + ]); + +} diff --git a/nixos/modules/misc/ids.nix b/nixos/modules/misc/ids.nix index 39a24cfecc53..ab3cbcab0646 100644 --- a/nixos/modules/misc/ids.nix +++ b/nixos/modules/misc/ids.nix @@ -56,7 +56,7 @@ #dialout = 27; # unused polkituser = 28; #utmp = 29; # unused - ddclient = 30; + # ddclient = 30; # converted to DynamicUser = true davfs2 = 31; #disnix = 33; # unused osgi = 34; @@ -106,7 +106,7 @@ freenet = 79; ircd = 80; bacula = 81; - almir = 82; + #almir = 82; # removed 2018-03-25, the almir package was removed in 30291227f2411abaca097773eedb49b8f259e297 during 2017-08 deluge = 83; mysql = 84; rabbitmq = 85; @@ -305,6 +305,7 @@ hass = 286; monero = 287; ceph = 288; + duplicati = 289; # When adding a uid, make sure it doesn't match an existing gid. And don't use uids above 399! @@ -343,7 +344,7 @@ dialout = 27; #polkituser = 28; # currently unused, polkitd doesn't need a group utmp = 29; - ddclient = 30; + # ddclient = 30; # converted to DynamicUser = true davfs2 = 31; disnix = 33; osgi = 34; @@ -393,7 +394,7 @@ freenet = 79; ircd = 80; bacula = 81; - almir = 82; + #almir = 82; # removed 2018-03-25, the almir package was removed in 30291227f2411abaca097773eedb49b8f259e297 during 2017-08 deluge = 83; mysql = 84; rabbitmq = 85; @@ -578,6 +579,7 @@ hass = 286; monero = 287; ceph = 288; + duplicati = 289; # When adding a gid, make sure it doesn't match an existing # uid. Users and groups with the same name should have equal diff --git a/nixos/modules/misc/locate.nix b/nixos/modules/misc/locate.nix index 51953d1110c4..ce5765cf1978 100644 --- a/nixos/modules/misc/locate.nix +++ b/nixos/modules/misc/locate.nix @@ -97,7 +97,7 @@ in { Whether not to index bind mounts ''; }; - + }; config = mkIf cfg.enable { @@ -133,13 +133,26 @@ in { systemd.services.update-locatedb = { description = "Update Locate Database"; path = mkIf (!isMLocate) [ pkgs.su ]; + + # mlocate's updatedb takes flags via a configuration file or + # on the command line, but not by environment variable. script = + if isMLocate + then let toFlags = x: optional (cfg.${x} != []) + "--${lib.toLower x} '${concatStringsSep " " cfg.${x}}'"; + args = concatLists (map toFlags ["pruneFS" "pruneNames" "prunePaths"]); + in '' + exec ${cfg.locate}/bin/updatedb \ + --output ${toString cfg.output} ${concatStringsSep " " args} \ + --prune-bind-mounts ${if cfg.pruneBindMounts then "yes" else "no"} \ + ${concatStringsSep " " cfg.extraFlags} '' + else '' exec ${cfg.locate}/bin/updatedb \ ${optionalString (cfg.localuser != null && ! isMLocate) ''--localuser=${cfg.localuser}''} \ --output=${toString cfg.output} ${concatStringsSep " " cfg.extraFlags} ''; - environment = { + environment = optionalAttrs (!isMLocate) { PRUNEFS = concatStringsSep " " cfg.pruneFS; PRUNEPATHS = concatStringsSep " " cfg.prunePaths; PRUNENAMES = concatStringsSep " " cfg.pruneNames; diff --git a/nixos/modules/misc/nixpkgs.nix b/nixos/modules/misc/nixpkgs.nix index 11bd148d5dee..8fbe218b232a 100644 --- a/nixos/modules/misc/nixpkgs.nix +++ b/nixos/modules/misc/nixpkgs.nix @@ -33,7 +33,11 @@ let configType = mkOptionType { name = "nixpkgs-config"; description = "nixpkgs config"; - check = traceValIfNot isConfig; + check = x: + let traceXIfNot = c: + if c x then true + else lib.traceSeqN 1 x false; + in traceXIfNot isConfig; merge = args: fold (def: mergeConfig def.value) {}; }; @@ -58,10 +62,13 @@ in pkgs = mkOption { defaultText = literalExample ''import "''${nixos}/.." { - inherit (config.nixpkgs) config overlays system; + inherit (config.nixpkgs) config overlays localSystem crossSystem; } ''; - default = import ../../.. { inherit (cfg) config overlays system; }; + default = import ../../.. { + localSystem = { inherit (cfg) system; } // cfg.localSystem; + inherit (cfg) config overlays crossSystem; + }; type = pkgsType; example = literalExample ''import <nixpkgs> {}''; description = '' @@ -73,8 +80,9 @@ in relative to the location of this NixOS module, because NixOS and Nixpkgs are distributed together for consistency, so the <code>nixos</code> in the default value is in fact a - relative path. The <code>config</code>, <code>overlays</code> - and <code>system</code> come from this option's siblings. + relative path. The <code>config</code>, <code>overlays</code>, + <code>localSystem</code>, and <code>crossSystem</code> come + from this option's siblings. This option can be used by applications like NixOps to increase the performance of evaluation, or to create packages that depend @@ -130,14 +138,63 @@ in ''; }; + localSystem = mkOption { + type = types.attrs; # TODO utilize lib.systems.parsedPlatform + default = { system = builtins.currentSystem; }; + example = { system = "aarch64-linux"; config = "aarch64-unknown-linux-gnu"; }; + defaultText = literalExample + ''(import "''${nixos}/../lib").lib.systems.examples.aarch64-multiplatform''; + description = '' + Specifies the platform on which NixOS should be built. When + <code>nixpkgs.crossSystem</code> is unset, it also specifies + the platform <emphasis>for</emphasis> which NixOS should be + built. If this option is unset, it defaults to the platform + type of the machine where evaluation happens. Specifying this + option is useful when doing distributed multi-platform + deployment, or when building virtual machines. See its + description in the Nixpkgs manual for more details. + + Ignored when <code>nixpkgs.pkgs</code> is set. + ''; + }; + + crossSystem = mkOption { + type = types.nullOr types.attrs; # TODO utilize lib.systems.parsedPlatform + default = null; + example = { system = "aarch64-linux"; config = "aarch64-unknown-linux-gnu"; }; + defaultText = literalExample + ''(import "''${nixos}/../lib").lib.systems.examples.aarch64-multiplatform''; + description = '' + Specifies the platform for which NixOS should be + built. Specify this only if it is different from + <code>nixpkgs.localSystem</code>, the platform + <emphasis>on</emphasis> which NixOS should be built. In other + words, specify this to cross-compile NixOS. Otherwise it + should be set as null, the default. See its description in the + Nixpkgs manual for more details. + + Ignored when <code>nixpkgs.pkgs</code> is set. + ''; + }; + system = mkOption { type = types.str; example = "i686-linux"; description = '' - Specifies the Nix platform type for which NixOS should be built. - If unset, it defaults to the platform type of your host system. - Specifying this option is useful when doing distributed - multi-platform deployment, or when building virtual machines. + Specifies the Nix platform type on which NixOS should be built. + It is better to specify <code>nixpkgs.localSystem</code> instead. + <programlisting> + { + nixpkgs.system = ..; + } + </programlisting> + is the same as + <programlisting> + { + nixpkgs.localSystem.system = ..; + } + </programlisting> + See <code>nixpkgs.localSystem</code> for more information. Ignored when <code>nixpkgs.pkgs</code> is set. ''; diff --git a/nixos/modules/misc/version.nix b/nixos/modules/misc/version.nix index 6af584250a70..74c86443ab90 100644 --- a/nixos/modules/misc/version.nix +++ b/nixos/modules/misc/version.nix @@ -5,8 +5,6 @@ with lib; let cfg = config.system.nixos; - releaseFile = "${toString pkgs.path}/.version"; - suffixFile = "${toString pkgs.path}/.version-suffix"; revisionFile = "${toString pkgs.path}/.git-revision"; gitRepo = "${toString pkgs.path}/.git"; gitCommitId = lib.substring 0 7 (commitIdFromGitRepo gitRepo); @@ -25,14 +23,14 @@ in nixos.release = mkOption { readOnly = true; type = types.str; - default = fileContents releaseFile; + default = trivial.release; description = "The NixOS release (e.g. <literal>16.03</literal>)."; }; nixos.versionSuffix = mkOption { internal = true; type = types.str; - default = if pathExists suffixFile then fileContents suffixFile else "pre-git"; + default = trivial.versionSuffix; description = "The NixOS version suffix (e.g. <literal>1160.f2d4ee1</literal>)."; }; @@ -85,8 +83,8 @@ in revision = mkIf (pathIsDirectory gitRepo) (mkDefault gitCommitId); versionSuffix = mkIf (pathIsDirectory gitRepo) (mkDefault (".git." + gitCommitId)); - # Note: code names must only increase in alphabetical order. - codeName = "Impala"; + # Note: the first letter is bumped on every release. It's an animal. + codeName = "Jellyfish"; }; # Generate /etc/os-release. See diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index e7f28c670bed..b0889e62f7c5 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -41,6 +41,7 @@ ./hardware/pcmcia.nix ./hardware/raid/hpsa.nix ./hardware/usb-wwan.nix + ./hardware/onlykey.nix ./hardware/video/amdgpu.nix ./hardware/video/amdgpu-pro.nix ./hardware/video/ati.nix @@ -58,6 +59,7 @@ ./installer/tools/tools.nix ./misc/assertions.nix ./misc/crashdump.nix + ./misc/documentation.nix ./misc/extra-arguments.nix ./misc/ids.nix ./misc/lib.nix @@ -85,12 +87,11 @@ ./programs/freetds.nix ./programs/gnupg.nix ./programs/gphoto2.nix - ./programs/info.nix + ./programs/iftop.nix ./programs/java.nix ./programs/kbdlight.nix ./programs/less.nix ./programs/light.nix - ./programs/man.nix ./programs/mosh.nix ./programs/mtr.nix ./programs/nano.nix @@ -104,6 +105,7 @@ ./programs/shadow.nix ./programs/shell.nix ./programs/spacefm.nix + ./programs/singularity.nix ./programs/ssh.nix ./programs/ssmtp.nix ./programs/sysdig.nix @@ -157,12 +159,14 @@ ./services/audio/slimserver.nix ./services/audio/squeezelite.nix ./services/audio/ympd.nix - ./services/backup/almir.nix ./services/backup/bacula.nix + ./services/backup/borgbackup.nix + ./services/backup/duplicati.nix ./services/backup/crashplan.nix ./services/backup/crashplan-small-business.nix ./services/backup/mysql-backup.nix ./services/backup/postgresql-backup.nix + ./services/backup/restic.nix ./services/backup/rsnapshot.nix ./services/backup/tarsnap.nix ./services/backup/znapzend.nix @@ -191,6 +195,7 @@ ./services/databases/clickhouse.nix ./services/databases/couchdb.nix ./services/databases/firebird.nix + ./services/databases/foundationdb.nix ./services/databases/hbase.nix ./services/databases/influxdb.nix ./services/databases/memcached.nix @@ -245,6 +250,7 @@ ./services/hardware/illum.nix ./services/hardware/interception-tools.nix ./services/hardware/irqbalance.nix + ./services/hardware/lcd.nix ./services/hardware/nvidia-optimus.nix ./services/hardware/pcscd.nix ./services/hardware/pommed.nix @@ -324,6 +330,7 @@ #./services/misc/gitit.nix ./services/misc/gitlab.nix ./services/misc/gitolite.nix + ./services/misc/gitweb.nix ./services/misc/gogs.nix ./services/misc/gollum.nix ./services/misc/gpsd.nix @@ -361,6 +368,8 @@ ./services/misc/rippled.nix ./services/misc/ripple-data-api.nix ./services/misc/rogue.nix + ./services/misc/serviio.nix + ./services/misc/safeeyes.nix ./services/misc/siproxd.nix ./services/misc/snapper.nix ./services/misc/sonarr.nix @@ -396,16 +405,7 @@ ./services/monitoring/osquery.nix ./services/monitoring/prometheus/default.nix ./services/monitoring/prometheus/alertmanager.nix - ./services/monitoring/prometheus/blackbox-exporter.nix - ./services/monitoring/prometheus/collectd-exporter.nix - ./services/monitoring/prometheus/fritzbox-exporter.nix - ./services/monitoring/prometheus/json-exporter.nix - ./services/monitoring/prometheus/minio-exporter.nix - ./services/monitoring/prometheus/nginx-exporter.nix - ./services/monitoring/prometheus/node-exporter.nix - ./services/monitoring/prometheus/snmp-exporter.nix - ./services/monitoring/prometheus/unifi-exporter.nix - ./services/monitoring/prometheus/varnish-exporter.nix + ./services/monitoring/prometheus/exporters.nix ./services/monitoring/riemann.nix ./services/monitoring/riemann-dash.nix ./services/monitoring/riemann-tools.nix @@ -483,6 +483,7 @@ ./services/networking/gnunet.nix ./services/networking/gogoclient.nix ./services/networking/gvpe.nix + ./services/networking/hans.nix ./services/networking/haproxy.nix ./services/networking/heyefi.nix ./services/networking/hostapd.nix @@ -536,7 +537,7 @@ ./services/networking/prayer.nix ./services/networking/privoxy.nix ./services/networking/prosody.nix - # ./services/networking/quagga.nix + ./services/networking/quagga.nix ./services/networking/quassel.nix ./services/networking/racoon.nix ./services/networking/radicale.nix @@ -550,6 +551,7 @@ ./services/networking/searx.nix ./services/networking/seeks.nix ./services/networking/skydns.nix + ./services/networking/shadowsocks.nix ./services/networking/shairport-sync.nix ./services/networking/shout.nix ./services/networking/sniproxy.nix @@ -561,6 +563,7 @@ ./services/networking/ssh/lshd.nix ./services/networking/ssh/sshd.nix ./services/networking/strongswan.nix + ./services/networking/strongswan-swanctl/module.nix ./services/networking/stunnel.nix ./services/networking/supplicant.nix ./services/networking/supybot.nix @@ -638,7 +641,6 @@ ./services/web-apps/atlassian/jira.nix ./services/web-apps/frab.nix ./services/web-apps/mattermost.nix - ./services/web-apps/nixbot.nix ./services/web-apps/nexus.nix ./services/web-apps/pgpkeyserver-lite.nix ./services/web-apps/matomo.nix @@ -658,6 +660,7 @@ ./services/web-servers/mighttpd2.nix ./services/web-servers/minio.nix ./services/web-servers/nginx/default.nix + ./services/web-servers/nginx/gitweb.nix ./services/web-servers/phpfpm/default.nix ./services/web-servers/shellinabox.nix ./services/web-servers/tomcat.nix diff --git a/nixos/modules/profiles/base.nix b/nixos/modules/profiles/base.nix index 39b8553976eb..3bf06a951193 100644 --- a/nixos/modules/profiles/base.nix +++ b/nixos/modules/profiles/base.nix @@ -9,7 +9,7 @@ environment.systemPackages = [ pkgs.w3m-nox # needed for the manual anyway pkgs.testdisk # useful for repairing boot problems - pkgs.mssys # for writing Microsoft boot sectors / MBRs + pkgs.ms-sys # for writing Microsoft boot sectors / MBRs pkgs.efibootmgr pkgs.efivar pkgs.parted diff --git a/nixos/modules/profiles/demo.nix b/nixos/modules/profiles/demo.nix index ef6fd77b5f8d..c3ee6e98371e 100644 --- a/nixos/modules/profiles/demo.nix +++ b/nixos/modules/profiles/demo.nix @@ -10,4 +10,10 @@ password = "demo"; uid = 1000; }; + + services.xserver.displayManager.sddm.autoLogin = { + enable = true; + relogin = true; + user = "demo"; + }; } diff --git a/nixos/modules/profiles/docker-container.nix b/nixos/modules/profiles/docker-container.nix index 433492b96137..7031d7d1d593 100644 --- a/nixos/modules/profiles/docker-container.nix +++ b/nixos/modules/profiles/docker-container.nix @@ -14,9 +14,7 @@ in { ]; # Create the tarball - system.build.tarball = import ../../lib/make-system-tarball.nix { - inherit (pkgs) stdenv perl xz pathsFromGraph; - + system.build.tarball = pkgs.callPackage ../../lib/make-system-tarball.nix { contents = []; extraArgs = "--owner=0"; diff --git a/nixos/modules/profiles/minimal.nix b/nixos/modules/profiles/minimal.nix index e2497d04252e..40df7063a9bf 100644 --- a/nixos/modules/profiles/minimal.nix +++ b/nixos/modules/profiles/minimal.nix @@ -10,10 +10,9 @@ with lib; # This isn't perfect, but let's expect the user specifies an UTF-8 defaultLocale i18n.supportedLocales = [ (config.i18n.defaultLocale + "/UTF-8") ]; - services.nixosManual.enable = mkDefault false; - programs.man.enable = mkDefault false; - programs.info.enable = mkDefault false; + documentation.enable = mkDefault false; + services.nixosManual.enable = mkDefault false; sound.enable = mkDefault false; } diff --git a/nixos/modules/programs/bash/bash.nix b/nixos/modules/programs/bash/bash.nix index c0967316c0c7..69a1a482d074 100644 --- a/nixos/modules/programs/bash/bash.nix +++ b/nixos/modules/programs/bash/bash.nix @@ -110,7 +110,7 @@ in }; enableCompletion = mkOption { - default = false; + default = true; description = '' Enable Bash completion for all interactive bash shells. ''; diff --git a/nixos/modules/programs/iftop.nix b/nixos/modules/programs/iftop.nix new file mode 100644 index 000000000000..a98a9a8187d4 --- /dev/null +++ b/nixos/modules/programs/iftop.nix @@ -0,0 +1,18 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let + cfg = config.programs.iftop; +in { + options = { + programs.iftop.enable = mkEnableOption "iftop + setcap wrapper"; + }; + config = mkIf cfg.enable { + environment.systemPackages = [ pkgs.iftop ]; + security.wrappers.iftop = { + source = "${pkgs.iftop}/bin/iftop"; + capabilities = "cap_net_raw+p"; + }; + }; +} diff --git a/nixos/modules/programs/info.nix b/nixos/modules/programs/info.nix deleted file mode 100644 index be6439dca5ad..000000000000 --- a/nixos/modules/programs/info.nix +++ /dev/null @@ -1,30 +0,0 @@ -{ config, lib, pkgs, ... }: - -with lib; - -{ - - options = { - - programs.info.enable = mkOption { - type = types.bool; - default = true; - description = '' - Whether to enable info pages and the <command>info</command> command. - ''; - }; - - }; - - - config = mkIf config.programs.info.enable { - - environment.systemPackages = [ pkgs.texinfoInteractive ]; - - environment.pathsToLink = [ "/info" "/share/info" ]; - - environment.extraOutputsToInstall = [ "info" ]; - - }; - -} diff --git a/nixos/modules/programs/less.nix b/nixos/modules/programs/less.nix index c0283c9e6862..d39103a58057 100644 --- a/nixos/modules/programs/less.nix +++ b/nixos/modules/programs/less.nix @@ -6,7 +6,7 @@ let cfg = config.programs.less; - configFile = '' + configText = if (cfg.configFile != null) then (builtins.readFile cfg.configFile) else '' #command ${concatStringsSep "\n" (mapAttrsToList (command: action: "${command} ${action}") cfg.commands) @@ -25,7 +25,7 @@ let ''; lessKey = pkgs.runCommand "lesskey" - { src = pkgs.writeText "lessconfig" configFile; } + { src = pkgs.writeText "lessconfig" configText; } "${pkgs.less}/bin/lesskey -o $out $src"; in @@ -37,6 +37,19 @@ in enable = mkEnableOption "less"; + configFile = mkOption { + type = types.nullOr types.path; + default = null; + example = literalExample "$${pkgs.my-configs}/lesskey"; + description = '' + Path to lesskey configuration file. + + <option>configFile</option> takes precedence over <option>commands</option>, + <option>clearDefaultCommands</option>, <option>lineEditingKeys</option>, and + <option>envVariables</option>. + ''; + }; + commands = mkOption { type = types.attrsOf types.str; default = {}; diff --git a/nixos/modules/programs/man.nix b/nixos/modules/programs/man.nix deleted file mode 100644 index 5b20a38d8856..000000000000 --- a/nixos/modules/programs/man.nix +++ /dev/null @@ -1,31 +0,0 @@ -{ config, lib, pkgs, ... }: - -with lib; - -{ - - options = { - - programs.man.enable = mkOption { - type = types.bool; - default = true; - description = '' - Whether to enable manual pages and the <command>man</command> command. - This also includes "man" outputs of all <literal>systemPackages</literal>. - ''; - }; - - }; - - - config = mkIf config.programs.man.enable { - - environment.systemPackages = [ pkgs.man-db ]; - - environment.pathsToLink = [ "/share/man" ]; - - environment.extraOutputsToInstall = [ "man" ]; - - }; - -} diff --git a/nixos/modules/programs/rootston.nix b/nixos/modules/programs/rootston.nix index 1946b1db657b..842d9e6cfb48 100644 --- a/nixos/modules/programs/rootston.nix +++ b/nixos/modules/programs/rootston.nix @@ -6,7 +6,7 @@ let cfg = config.programs.rootston; rootstonWrapped = pkgs.writeScriptBin "rootston" '' - #! ${pkgs.stdenv.shell} + #! ${pkgs.runtimeShell} if [[ "$#" -ge 1 ]]; then exec ${pkgs.rootston}/bin/rootston "$@" else diff --git a/nixos/modules/programs/singularity.nix b/nixos/modules/programs/singularity.nix new file mode 100644 index 000000000000..86153d933855 --- /dev/null +++ b/nixos/modules/programs/singularity.nix @@ -0,0 +1,20 @@ +{ config, pkgs, lib, ... }: + +with lib; +let + cfg = config.programs.singularity; +in { + options.programs.singularity = { + enable = mkEnableOption "Singularity"; + }; + + config = mkIf cfg.enable { + environment.systemPackages = [ pkgs.singularity ]; + systemd.tmpfiles.rules = [ "d /var/singularity/mnt/session 0770 root root -" + "d /var/singularity/mnt/final 0770 root root -" + "d /var/singularity/mnt/overlay 0770 root root -" + "d /var/singularity/mnt/container 0770 root root -" + "d /var/singularity/mnt/source 0770 root root -"]; + }; + +} diff --git a/nixos/modules/programs/ssh.nix b/nixos/modules/programs/ssh.nix index 0935bf0cae71..36289080a82a 100644 --- a/nixos/modules/programs/ssh.nix +++ b/nixos/modules/programs/ssh.nix @@ -13,7 +13,7 @@ let askPasswordWrapper = pkgs.writeScript "ssh-askpass-wrapper" '' - #! ${pkgs.stdenv.shell} -e + #! ${pkgs.runtimeShell} -e export DISPLAY="$(systemctl --user show-environment | ${pkgs.gnused}/bin/sed 's/^DISPLAY=\(.*\)/\1/; t; d')" exec ${askPassword} ''; diff --git a/nixos/modules/rename.nix b/nixos/modules/rename.nix index 489d7d8b9b50..a1ead80cc215 100644 --- a/nixos/modules/rename.nix +++ b/nixos/modules/rename.nix @@ -4,6 +4,7 @@ with lib; { imports = [ + (mkRenamedOptionModule [ "dysnomia" ] [ "services" "dysnomia" ]) (mkRenamedOptionModule [ "environment" "x11Packages" ] [ "environment" "systemPackages" ]) (mkRenamedOptionModule [ "environment" "enableBashCompletion" ] [ "programs" "bash" "enableCompletion" ]) (mkRenamedOptionModule [ "environment" "nix" ] [ "nix" "package" ]) @@ -22,6 +23,8 @@ with lib; (config: let enabled = getAttrFromPath [ "services" "printing" "gutenprint" ] config; in if enabled then [ pkgs.gutenprint ] else [ ])) + (mkRenamedOptionModule [ "services" "ddclient" "domain" ] [ "services" "ddclient" "domains" ]) + (mkRemovedOptionModule [ "services" "ddclient" "homeDir" ] "") (mkRenamedOptionModule [ "services" "elasticsearch" "host" ] [ "services" "elasticsearch" "listenAddress" ]) (mkRenamedOptionModule [ "services" "graphite" "api" "host" ] [ "services" "graphite" "api" "listenAddress" ]) (mkRenamedOptionModule [ "services" "graphite" "web" "host" ] [ "services" "graphite" "web" "listenAddress" ]) @@ -186,22 +189,24 @@ with lib; (mkRenamedOptionModule [ "services" "xserver" "desktopManager" "kde5" ] [ "services" "xserver" "desktopManager" "plasma5" ]) # Fontconfig - (mkRenamedOptionModule [ "config" "fonts" "fontconfig" "ultimate" "allowBitmaps" ] [ "config" "fonts" "fontconfig" "allowBitmaps" ]) - (mkRenamedOptionModule [ "config" "fonts" "fontconfig" "ultimate" "allowType1" ] [ "config" "fonts" "fontconfig" "allowType1" ]) - (mkRenamedOptionModule [ "config" "fonts" "fontconfig" "ultimate" "useEmbeddedBitmaps" ] [ "config" "fonts" "fontconfig" "useEmbeddedBitmaps" ]) - (mkRenamedOptionModule [ "config" "fonts" "fontconfig" "ultimate" "forceAutohint" ] [ "config" "fonts" "fontconfig" "forceAutohint" ]) - (mkRenamedOptionModule [ "config" "fonts" "fontconfig" "ultimate" "renderMonoTTFAsBitmap" ] [ "config" "fonts" "fontconfig" "renderMonoTTFAsBitmap" ]) + (mkRenamedOptionModule [ "fonts" "fontconfig" "ultimate" "allowBitmaps" ] [ "fonts" "fontconfig" "allowBitmaps" ]) + (mkRenamedOptionModule [ "fonts" "fontconfig" "ultimate" "allowType1" ] [ "fonts" "fontconfig" "allowType1" ]) + (mkRenamedOptionModule [ "fonts" "fontconfig" "ultimate" "useEmbeddedBitmaps" ] [ "fonts" "fontconfig" "useEmbeddedBitmaps" ]) + (mkRenamedOptionModule [ "fonts" "fontconfig" "ultimate" "forceAutohint" ] [ "fonts" "fontconfig" "forceAutohint" ]) + (mkRenamedOptionModule [ "fonts" "fontconfig" "ultimate" "renderMonoTTFAsBitmap" ] [ "fonts" "fontconfig" "renderMonoTTFAsBitmap" ]) # Profile splitting (mkRenamedOptionModule [ "virtualization" "growPartition" ] [ "boot" "growPartition" ]) # misc/version.nix - (mkRenamedOptionModule [ "config" "system" "nixosVersion" ] [ "config" "system" "nixos" "version" ]) - (mkRenamedOptionModule [ "config" "system" "nixosRelease" ] [ "config" "system" "nixos" "release" ]) - (mkRenamedOptionModule [ "config" "system" "nixosVersionSuffix" ] [ "config" "system" "nixos" "versionSuffix" ]) - (mkRenamedOptionModule [ "config" "system" "nixosRevision" ] [ "config" "system" "nixos" "revision" ]) - (mkRenamedOptionModule [ "config" "system" "nixosCodeName" ] [ "config" "system" "nixos" "codeName" ]) - (mkRenamedOptionModule [ "config" "system" "nixosLabel" ] [ "config" "system" "nixos" "label" ]) + (mkRenamedOptionModule [ "system" "nixosVersion" ] [ "system" "nixos" "version" ]) + (mkRenamedOptionModule [ "system" "nixosVersionSuffix" ] [ "system" "nixos" "versionSuffix" ]) + (mkRenamedOptionModule [ "system" "nixosRevision" ] [ "system" "nixos" "revision" ]) + (mkRenamedOptionModule [ "system" "nixosLabel" ] [ "system" "nixos" "label" ]) + + # Users + (mkAliasOptionModule [ "users" "extraUsers" ] [ "users" "users" ]) + (mkAliasOptionModule [ "users" "extraGroups" ] [ "users" "groups" ]) # Options that are obsolete and have no replacement. (mkRemovedOptionModule [ "boot" "initrd" "luks" "enable" ] "") @@ -240,5 +245,15 @@ with lib; # Xen (mkRenamedOptionModule [ "virtualisation" "xen" "qemu-package" ] [ "virtualisation" "xen" "package-qemu" ]) - ]; + + (mkRenamedOptionModule [ "programs" "info" "enable" ] [ "documentation" "info" "enable" ]) + (mkRenamedOptionModule [ "programs" "man" "enable" ] [ "documentation" "man" "enable" ]) + + ] ++ (flip map [ "blackboxExporter" "collectdExporter" "fritzboxExporter" + "jsonExporter" "minioExporter" "nginxExporter" "nodeExporter" + "snmpExporter" "unifiExporter" "varnishExporter" ] + (opt: mkRemovedOptionModule [ "services" "prometheus" "${opt}" ] '' + The prometheus exporters are now configured using `services.prometheus.exporters'. + See the 18.03 release notes for more information. + '' )); } diff --git a/nixos/modules/security/acme.nix b/nixos/modules/security/acme.nix index 0736239ed2cf..e430c2ddb903 100644 --- a/nixos/modules/security/acme.nix +++ b/nixos/modules/security/acme.nix @@ -58,9 +58,11 @@ let default = ""; example = "systemctl reload nginx.service"; description = '' - Commands to run after certificates are re-issued. Typically + Commands to run after new certificates go live. Typically the web server and other servers using certificates need to be reloaded. + + Executed in the same directory with the new certificate. ''; }; @@ -78,6 +80,27 @@ let ''; }; + activationDelay = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + Systemd time span expression to delay copying new certificates to main + state directory. See <citerefentry><refentrytitle>systemd.time</refentrytitle> + <manvolnum>7</manvolnum></citerefentry>. + ''; + }; + + preDelay = mkOption { + type = types.lines; + default = ""; + description = '' + Commands to run after certificates are re-issued but before they are + activated. Typically the new certificate is published to DNS. + + Executed in the same directory with the new certificate. + ''; + }; + extraDomains = mkOption { type = types.attrsOf (types.nullOr types.str); default = {}; @@ -140,14 +163,6 @@ in ''; }; - tosHash = mkOption { - type = types.string; - default = "cc88d8d9517f490191401e7b54e9ffd12a2b9082ec7a1d4cec6101f9f1647e7b"; - description = '' - SHA256 of the Terms of Services document. This changes once in a while. - ''; - }; - production = mkOption { type = types.bool; default = true; @@ -194,14 +209,15 @@ in servicesLists = mapAttrsToList certToServices cfg.certs; certToServices = cert: data: let - cpath = "${cfg.directory}/${cert}"; + domain = if data.domain != null then data.domain else cert; + cpath = lpath + optionalString (data.activationDelay != null) ".staging"; + lpath = "${cfg.directory}/${cert}"; rights = if data.allowKeysForGroup then "750" else "700"; - cmdline = [ "-v" "-d" data.domain "--default_root" data.webroot "--valid_min" cfg.validMin "--tos_sha256" cfg.tosHash ] + cmdline = [ "-v" "-d" data.domain "--default_root" data.webroot "--valid_min" cfg.validMin ] ++ optionals (data.email != null) [ "--email" data.email ] ++ concatMap (p: [ "-f" p ]) data.plugins ++ concatLists (mapAttrsToList (name: root: [ "-d" (if root == null then name else "${name}:${root}")]) data.extraDomains) - ++ (if cfg.production then [] - else ["--server" "https://acme-staging.api.letsencrypt.org/directory"]); + ++ optionals (!cfg.production) ["--server" "https://acme-staging.api.letsencrypt.org/directory"]; acmeService = { description = "Renew ACME Certificate for ${cert}"; after = [ "network.target" "network-online.target" ]; @@ -214,7 +230,7 @@ in Group = data.group; PrivateTmp = true; }; - path = [ pkgs.simp_le ]; + path = with pkgs; [ simp_le systemd ]; preStart = '' mkdir -p '${cfg.directory}' chown 'root:root' '${cfg.directory}' @@ -237,17 +253,39 @@ in exit "$EXITCODE" ''; postStop = '' + cd '${cpath}' + if [ -e /tmp/lastExitCode ] && [ "$(cat /tmp/lastExitCode)" = "0" ]; then - echo "Executing postRun hook..." - ${data.postRun} + ${if data.activationDelay != null then '' + + ${data.preDelay} + + if [ -d '${lpath}' ]; then + systemd-run --no-block --on-active='${data.activationDelay}' --unit acme-setlive-${cert}.service + else + systemctl --wait start acme-setlive-${cert}.service + fi + '' else data.postRun} fi ''; before = [ "acme-certificates.target" ]; wantedBy = [ "acme-certificates.target" ]; }; + delayService = { + description = "Set certificate for ${cert} live"; + path = with pkgs; [ rsync ]; + serviceConfig = { + Type = "oneshot"; + }; + script = '' + rsync -a --delete-after '${cpath}/' '${lpath}' + ''; + postStop = data.postRun; + }; selfsignedService = { description = "Create preliminary self-signed certificate for ${cert}"; + path = [ pkgs.openssl ]; preStart = '' if [ ! -d '${cpath}' ] then @@ -258,37 +296,41 @@ in ''; script = '' - # Create self-signed key - workdir="/run/acme-selfsigned-${cert}" - ${pkgs.openssl.bin}/bin/openssl genrsa -des3 -passout pass:x -out $workdir/server.pass.key 2048 - ${pkgs.openssl.bin}/bin/openssl rsa -passin pass:x -in $workdir/server.pass.key -out $workdir/server.key - ${pkgs.openssl.bin}/bin/openssl req -new -key $workdir/server.key -out $workdir/server.csr \ + workdir="$(mktemp -d)" + + # Create CA + openssl genrsa -des3 -passout pass:x -out $workdir/ca.pass.key 2048 + openssl rsa -passin pass:x -in $workdir/ca.pass.key -out $workdir/ca.key + openssl req -new -key $workdir/ca.key -out $workdir/ca.csr \ + -subj "/C=UK/ST=Warwickshire/L=Leamington/O=OrgName/OU=Security Department/CN=example.com" + openssl x509 -req -days 1 -in $workdir/ca.csr -signkey $workdir/ca.key -out $workdir/ca.crt + + # Create key + openssl genrsa -des3 -passout pass:x -out $workdir/server.pass.key 2048 + openssl rsa -passin pass:x -in $workdir/server.pass.key -out $workdir/server.key + openssl req -new -key $workdir/server.key -out $workdir/server.csr \ -subj "/C=UK/ST=Warwickshire/L=Leamington/O=OrgName/OU=IT Department/CN=example.com" - ${pkgs.openssl.bin}/bin/openssl x509 -req -days 1 -in $workdir/server.csr -signkey $workdir/server.key -out $workdir/server.crt + openssl x509 -req -days 1 -in $workdir/server.csr -CA $workdir/ca.crt \ + -CAkey $workdir/ca.key -CAserial $workdir/ca.srl -CAcreateserial \ + -out $workdir/server.crt - # Move key to destination - mv $workdir/server.key ${cpath}/key.pem - mv $workdir/server.crt ${cpath}/fullchain.pem + # Copy key to destination + cp $workdir/server.key ${cpath}/key.pem - # Create full.pem for e.g. lighttpd (same format as "simp_le ... -f full.pem" creates) - cat "${cpath}/key.pem" "${cpath}/fullchain.pem" > "${cpath}/full.pem" + # Create fullchain.pem (same format as "simp_le ... -f fullchain.pem" creates) + cat $workdir/{server.crt,ca.crt} > "${cpath}/fullchain.pem" - # Clean up working directory - rm $workdir/server.csr - rm $workdir/server.pass.key + # Create full.pem for e.g. lighttpd + cat $workdir/{server.key,server.crt,ca.crt} > "${cpath}/full.pem" # Give key acme permissions - chmod ${rights} '${cpath}/key.pem' - chown '${data.user}:${data.group}' '${cpath}/key.pem' - chmod ${rights} '${cpath}/fullchain.pem' - chown '${data.user}:${data.group}' '${cpath}/fullchain.pem' - chmod ${rights} '${cpath}/full.pem' - chown '${data.user}:${data.group}' '${cpath}/full.pem' + chown '${data.user}:${data.group}' "${cpath}/"{key,fullchain,full}.pem + chmod ${rights} "${cpath}/"{key,fullchain,full}.pem ''; serviceConfig = { Type = "oneshot"; - RuntimeDirectory = "acme-selfsigned-${cert}"; PermissionsStartOnly = true; + PrivateTmp = true; User = data.user; Group = data.group; }; @@ -305,11 +347,8 @@ in }; in ( [ { name = "acme-${cert}"; value = acmeService; } ] - ++ - (if cfg.preliminarySelfsigned - then [ { name = "acme-selfsigned-${cert}"; value = selfsignedService; } ] - else [] - ) + ++ optional cfg.preliminarySelfsigned { name = "acme-selfsigned-${cert}"; value = selfsignedService; } + ++ optional (data.activationDelay != null) { name = "acme-setlive-${cert}"; value = delayService; } ); servicesAttr = listToAttrs services; injectServiceDep = { diff --git a/nixos/modules/security/audit.nix b/nixos/modules/security/audit.nix index 7ac21fd96507..2b22bdd9f0ae 100644 --- a/nixos/modules/security/audit.nix +++ b/nixos/modules/security/audit.nix @@ -13,7 +13,7 @@ let }; disableScript = pkgs.writeScript "audit-disable" '' - #!${pkgs.stdenv.shell} -eu + #!${pkgs.runtimeShell} -eu # Explicitly disable everything, as otherwise journald might start it. auditctl -D auditctl -e 0 -a task,never @@ -23,7 +23,7 @@ let # put in the store like this. At the same time, it doesn't feel like a huge deal and working # around that is a pain so I'm leaving it like this for now. startScript = pkgs.writeScript "audit-start" '' - #!${pkgs.stdenv.shell} -eu + #!${pkgs.runtimeShell} -eu # Clear out any rules we may start with auditctl -D @@ -43,7 +43,7 @@ let ''; stopScript = pkgs.writeScript "audit-stop" '' - #!${pkgs.stdenv.shell} -eu + #!${pkgs.runtimeShell} -eu # Clear the rules auditctl -D diff --git a/nixos/modules/security/duosec.nix b/nixos/modules/security/duosec.nix index 9ca818e86ffa..df6108dede7c 100644 --- a/nixos/modules/security/duosec.nix +++ b/nixos/modules/security/duosec.nix @@ -25,14 +25,14 @@ let loginCfgFile = optional cfg.ssh.enable { source = pkgs.writeText "login_duo.conf" configFile; mode = "0600"; - uid = config.ids.uids.sshd; + user = "sshd"; target = "duo/login_duo.conf"; }; pamCfgFile = optional cfg.pam.enable { source = pkgs.writeText "pam_duo.conf" configFile; mode = "0600"; - uid = config.ids.uids.sshd; + user = "sshd"; target = "duo/pam_duo.conf"; }; in diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix index e1cad03e66e2..48998285d89d 100644 --- a/nixos/modules/security/pam.nix +++ b/nixos/modules/security/pam.nix @@ -234,6 +234,11 @@ let password, KDE will prompt separately after login. ''; }; + sssdStrictAccess = mkOption { + default = false; + type = types.bool; + description = "enforce sssd access control"; + }; enableGnomeKeyring = mkOption { default = false; @@ -264,11 +269,13 @@ let text = mkDefault ('' # Account management. - account sufficient pam_unix.so + account ${if cfg.sssdStrictAccess then "required" else "sufficient"} pam_unix.so ${optionalString use_ldap "account sufficient ${pam_ldap}/lib/security/pam_ldap.so"} - ${optionalString config.services.sssd.enable + ${optionalString (config.services.sssd.enable && cfg.sssdStrictAccess==false) "account sufficient ${pkgs.sssd}/lib/security/pam_sss.so"} + ${optionalString (config.services.sssd.enable && cfg.sssdStrictAccess) + "account [default=bad success=ok user_unknown=ignore] ${pkgs.sssd}/lib/security/pam_sss.so"} ${optionalString config.krb5.enable "account sufficient ${pam_krb5}/lib/security/pam_krb5.so"} @@ -386,7 +393,7 @@ let ${optionalString (cfg.enableGnomeKeyring) "session optional ${pkgs.gnome3.gnome-keyring}/lib/security/pam_gnome_keyring.so auto_start"} ${optionalString (config.virtualisation.lxc.lxcfs.enable) - "session optional ${pkgs.lxcfs}/lib/security/pam_cgfs.so -c freezer,memory,name=systemd,unified,cpuset"} + "session optional ${pkgs.lxc}/lib/security/pam_cgfs.so -c all"} ''); }; diff --git a/nixos/modules/security/sudo.nix b/nixos/modules/security/sudo.nix index a57f14bb5ae1..24283e1d6165 100644 --- a/nixos/modules/security/sudo.nix +++ b/nixos/modules/security/sudo.nix @@ -47,8 +47,8 @@ in default = true; description = '' - Whether users of the <code>wheel</code> group can execute - commands as super user without entering a password. + Whether users of the <code>wheel</code> group must + provide a password to run commands as super user via <command>sudo</command>. ''; }; @@ -215,7 +215,7 @@ in { src = pkgs.writeText "sudoers-in" cfg.configFile; } # Make sure that the sudoers file is syntactically valid. # (currently disabled - NIXOS-66) - "${pkgs.sudo}/sbin/visudo -f $src -c && cp $src $out"; + "${pkgs.buildPackages.sudo}/sbin/visudo -f $src -c && cp $src $out"; target = "sudoers"; mode = "0440"; }; diff --git a/nixos/modules/security/wrappers/wrapper.c b/nixos/modules/security/wrappers/wrapper.c index 7091e314bb22..494e9e93ac22 100644 --- a/nixos/modules/security/wrappers/wrapper.c +++ b/nixos/modules/security/wrappers/wrapper.c @@ -10,8 +10,8 @@ #include <errno.h> #include <linux/capability.h> #include <sys/capability.h> -#include <linux/prctl.h> #include <sys/prctl.h> +#include <limits.h> #include <cap-ng.h> // Make sure assertions are not compiled out, we use them to codify diff --git a/nixos/modules/services/audio/alsa.nix b/nixos/modules/services/audio/alsa.nix index e3e8bb28c58b..376aad66e236 100644 --- a/nixos/modules/services/audio/alsa.nix +++ b/nixos/modules/services/audio/alsa.nix @@ -54,6 +54,11 @@ in description = '' Whether to enable volume and capture control with keyboard media keys. + You want to leave this disabled if you run a desktop environment + like KDE, Gnome, Xfce, etc, as those handle such things themselves. + You might want to enable this if you run a minimalistic desktop + environment or work from bare linux ttys/framebuffers. + Enabling this will turn on <option>services.actkbd</option>. ''; }; diff --git a/nixos/modules/services/backup/almir.nix b/nixos/modules/services/backup/almir.nix deleted file mode 100644 index fbb4ff4034f1..000000000000 --- a/nixos/modules/services/backup/almir.nix +++ /dev/null @@ -1,173 +0,0 @@ -{ config, lib, pkgs, ... }: - -with lib; - -let - cfg = config.services.almir; - - bconsoleconf = pkgs.writeText "bconsole.conf" - '' - Director { - Name = ${cfg.director_name} - DIRport = ${toString cfg.director_port} - address = ${cfg.director_address} - Password = "${cfg.director_password}" - } - ''; - - productionini = pkgs.writeText "production.ini" - '' -[app:main] -use = egg:almir - -pyramid.reload_templates = false -pyramid.debug_authorization = false -pyramid.debug_notfound = false -pyramid.debug_routematch = false -pyramid.debug_templates = false -pyramid.default_locale_name = en -pyramid.includes = - pyramid_exclog -exclog.extra_info = true - -sqlalchemy.url = ${cfg.sqlalchemy_engine_url} -timezone = ${cfg.timezone} -bconsole_config = ${bconsoleconf} - -[server:main] -use = egg:waitress#main -host = 127.0.0.1 -port = ${toString cfg.port} - - -# Begin logging configuration - -[loggers] -keys = root, almir, sqlalchemy, exc_logger - -[handlers] -keys = console - -[formatters] -keys = generic - -[logger_root] -level = WARN -handlers = console - -[logger_almir] -level = WARN -handlers = -qualname = almir - -[logger_exc_logger] -level = ERROR -handlers = -qualname = exc_logger - -[logger_sqlalchemy] -level = WARN -handlers = -qualname = sqlalchemy.engine -# "level = INFO" logs SQL queries. -# "level = DEBUG" logs SQL queries and results. -# "level = WARN" logs neither. (Recommended for production systems.) - -[handler_console] -class = StreamHandler -args = (sys.stderr,) -level = NOTSET -formatter = generic - -[formatter_generic] -format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s - ''; -in { - options = { - services.almir = { - enable = mkOption { - type = types.bool; - default = false; - description = '' - Enable Almir web server. Also configures postgresql database and installs bacula. - ''; - }; - - port = mkOption { - default = 35000; - type = types.int; - description = '' - Port for Almir web server to listen on. - ''; - }; - - timezone = mkOption { - description = '' - Timezone as specified in https://en.wikipedia.org/wiki/List_of_tz_database_time_zones - ''; - example = "Europe/Ljubljana"; - }; - - sqlalchemy_engine_url = mkOption { - default = "postgresql:///bacula"; - example = '' - postgresql://bacula:bacula@localhost:5432/bacula - mysql+mysqlconnector://<user>:<password>@<hostname>/<database>' - sqlite:////var/lib/bacula/bacula.db' - ''; - description = '' - Define SQL database connection to bacula catalog as specified in http://docs.sqlalchemy.org/en/latest/core/engines.html#database-urls - ''; - }; - - director_name = mkOption { - description = '' - Name of the Director to connect with bconsole. - ''; - }; - - director_password = mkOption { - description = '' - Password for Director to connect with bconsole. - ''; - }; - - director_port = mkOption { - default = 9101; - type = types.int; - description = '' - Port for Director to connect with bconsole. - ''; - }; - - director_address = mkOption { - default = "127.0.0.1"; - description = '' - IP/Hostname for Director to connect with bconsole. - ''; - }; - }; - }; - - config = mkIf cfg.enable { - systemd.services.almir = { - after = [ "network.target" "postgresql.service" ]; - description = "Almir web app"; - wantedBy = [ "multi-user.target" ]; - path = [ pkgs.pythonPackages.almir ]; - environment.PYTHONPATH = "${pkgs.pythonPackages.almir}/lib/${pkgs.pythonPackages.python.libPrefix}/site-packages"; - serviceConfig.ExecStart = "${pkgs.pythonPackages.pyramid}/bin/pserve ${productionini}"; - }; - - environment.systemPackages = [ pkgs.pythonPackages.almir ]; - - users.extraUsers.almir = { - group = "almir"; - uid = config.ids.uids.almir; - createHome = true; - shell = "${pkgs.bash}/bin/bash"; - }; - - users.extraGroups.almir.gid = config.ids.gids.almir; - }; -} diff --git a/nixos/modules/services/backup/borgbackup.nix b/nixos/modules/services/backup/borgbackup.nix new file mode 100644 index 000000000000..1b730e0c2b76 --- /dev/null +++ b/nixos/modules/services/backup/borgbackup.nix @@ -0,0 +1,580 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + isLocalPath = x: + builtins.substring 0 1 x == "/" # absolute path + || builtins.substring 0 1 x == "." # relative path + || builtins.match "[.*:.*]" == null; # not machine:path + + mkExcludeFile = cfg: + # Write each exclude pattern to a new line + pkgs.writeText "excludefile" (concatStringsSep "\n" cfg.exclude); + + mkKeepArgs = cfg: + # If cfg.prune.keep e.g. has a yearly attribute, + # its content is passed on as --keep-yearly + concatStringsSep " " + (mapAttrsToList (x: y: "--keep-${x}=${toString y}") cfg.prune.keep); + + mkBackupScript = cfg: '' + on_exit() + { + exitStatus=$? + # Reset the EXIT handler, or else we're called again on 'exit' below + trap - EXIT + ${cfg.postHook} + exit $exitStatus + } + trap 'on_exit' INT TERM QUIT EXIT + + archiveName="${cfg.archiveBaseName}-$(date ${cfg.dateFormat})" + archiveSuffix="${optionalString cfg.appendFailedSuffix ".failed"}" + ${cfg.preHook} + '' + optionalString cfg.doInit '' + # Run borg init if the repo doesn't exist yet + if ! borg list > /dev/null; then + borg init \ + --encryption ${cfg.encryption.mode} \ + $extraInitArgs + ${cfg.postInit} + fi + '' + '' + borg create \ + --compression ${cfg.compression} \ + --exclude-from ${mkExcludeFile cfg} \ + $extraCreateArgs \ + "::$archiveName$archiveSuffix" \ + ${escapeShellArgs cfg.paths} + '' + optionalString cfg.appendFailedSuffix '' + borg rename "::$archiveName$archiveSuffix" "$archiveName" + '' + '' + ${cfg.postCreate} + '' + optionalString (cfg.prune.keep != { }) '' + borg prune \ + ${mkKeepArgs cfg} \ + --prefix ${escapeShellArg cfg.prune.prefix} \ + $extraPruneArgs + ${cfg.postPrune} + ''; + + mkPassEnv = cfg: with cfg.encryption; + if passCommand != null then + { BORG_PASSCOMMAND = passCommand; } + else if passphrase != null then + { BORG_PASSPHRASE = passphrase; } + else { }; + + mkBackupService = name: cfg: + let + userHome = config.users.users.${cfg.user}.home; + in nameValuePair "borgbackup-job-${name}" { + description = "BorgBackup job ${name}"; + path = with pkgs; [ + borgbackup openssh + ]; + script = mkBackupScript cfg; + serviceConfig = { + User = cfg.user; + Group = cfg.group; + # Only run when no other process is using CPU or disk + CPUSchedulingPolicy = "idle"; + IOSchedulingClass = "idle"; + ProtectSystem = "strict"; + ReadWritePaths = + [ "${userHome}/.config/borg" "${userHome}/.cache/borg" ] + # Borg needs write access to repo if it is not remote + ++ optional (isLocalPath cfg.repo) cfg.repo; + PrivateTmp = true; + }; + environment = { + BORG_REPO = cfg.repo; + inherit (cfg) extraInitArgs extraCreateArgs extraPruneArgs; + } // (mkPassEnv cfg) // cfg.environment; + inherit (cfg) startAt; + }; + + # Paths listed in ReadWritePaths must exist before service is started + mkActivationScript = name: cfg: + let + install = "install -o ${cfg.user} -g ${cfg.group}"; + in + nameValuePair "borgbackup-job-${name}" (stringAfter [ "users" ] ('' + # Eensure that the home directory already exists + # We can't assert createHome == true because that's not the case for root + cd "${config.users.users.${cfg.user}.home}" + ${install} -d .config/borg + ${install} -d .cache/borg + '' + optionalString (isLocalPath cfg.repo) '' + ${install} -d ${escapeShellArg cfg.repo} + '')); + + mkPassAssertion = name: cfg: { + assertion = with cfg.encryption; + mode != "none" -> passCommand != null || passphrase != null; + message = + "passCommand or passphrase has to be specified because" + + '' borgbackup.jobs.${name}.encryption != "none"''; + }; + + mkRepoService = name: cfg: + nameValuePair "borgbackup-repo-${name}" { + description = "Create BorgBackup repository ${name} directory"; + script = '' + mkdir -p ${escapeShellArg cfg.path} + chown ${cfg.user}:${cfg.group} ${escapeShellArg cfg.path} + ''; + serviceConfig = { + # The service's only task is to ensure that the specified path exists + Type = "oneshot"; + }; + wantedBy = [ "multi-user.target" ]; + }; + + mkAuthorizedKey = cfg: appendOnly: key: + let + # Because of the following line, clients do not need to specify an absolute repo path + cdCommand = "cd ${escapeShellArg cfg.path}"; + restrictedArg = "--restrict-to-${if cfg.allowSubRepos then "path" else "repository"} ."; + appendOnlyArg = optionalString appendOnly "--append-only"; + quotaArg = optionalString (cfg.quota != null) "--storage-quota ${cfg.quota}"; + serveCommand = "borg serve ${restrictedArg} ${appendOnlyArg} ${quotaArg}"; + in + ''command="${cdCommand} && ${serveCommand}",restrict ${key}''; + + mkUsersConfig = name: cfg: { + users.${cfg.user} = { + openssh.authorizedKeys.keys = + (map (mkAuthorizedKey cfg false) cfg.authorizedKeys + ++ map (mkAuthorizedKey cfg true) cfg.authorizedKeysAppendOnly); + useDefaultShell = true; + }; + groups.${cfg.group} = { }; + }; + + mkKeysAssertion = name: cfg: { + assertion = cfg.authorizedKeys != [ ] || cfg.authorizedKeysAppendOnly != [ ]; + message = + "borgbackup.repos.${name} does not make sense" + + " without at least one public key"; + }; + +in { + meta.maintainers = with maintainers; [ dotlambda ]; + + ###### interface + + options.services.borgbackup.jobs = mkOption { + description = "Deduplicating backups using BorgBackup."; + default = { }; + example = literalExample '' + { + rootBackup = { + paths = "/"; + exclude = [ "/nix" ]; + repo = "/path/to/local/repo"; + encryption = { + mode = "repokey"; + passphrase = "secret"; + }; + compression = "auto,lzma"; + startAt = "weekly"; + }; + } + ''; + type = types.attrsOf (types.submodule (let globalConfig = config; in + { name, config, ... }: { + options = { + + paths = mkOption { + type = with types; either path (nonEmptyListOf path); + description = "Path(s) to back up."; + example = "/home/user"; + apply = x: if isList x then x else [ x ]; + }; + + repo = mkOption { + type = types.str; + description = "Remote or local repository to back up to."; + example = "user@machine:/path/to/repo"; + }; + + archiveBaseName = mkOption { + type = types.strMatching "[^/{}]+"; + default = "${globalConfig.networking.hostName}-${name}"; + defaultText = "\${config.networking.hostName}-<name>"; + description = '' + How to name the created archives. A timestamp, whose format is + determined by <option>dateFormat</option>, will be appended. The full + name can be modified at runtime (<literal>$archiveName</literal>). + Placeholders like <literal>{hostname}</literal> must not be used. + ''; + }; + + dateFormat = mkOption { + type = types.str; + description = '' + Arguments passed to <command>date</command> + to create a timestamp suffix for the archive name. + ''; + default = "+%Y-%m-%dT%H:%M:%S"; + example = "-u +%s"; + }; + + startAt = mkOption { + type = with types; either str (listOf str); + default = "daily"; + description = '' + When or how often the backup should run. + Must be in the format described in + <citerefentry><refentrytitle>systemd.time</refentrytitle> + <manvolnum>7</manvolnum></citerefentry>. + If you do not want the backup to start + automatically, use <literal>[ ]</literal>. + ''; + }; + + user = mkOption { + type = types.str; + description = '' + The user <command>borg</command> is run as. + User or group need read permission + for the specified <option>paths</option>. + ''; + default = "root"; + }; + + group = mkOption { + type = types.str; + description = '' + The group borg is run as. User or group needs read permission + for the specified <option>paths</option>. + ''; + default = "root"; + }; + + encryption.mode = mkOption { + type = types.enum [ + "repokey" "keyfile" + "repokey-blake2" "keyfile-blake2" + "authenticated" "authenticated-blake2" + "none" + ]; + description = '' + Encryption mode to use. Setting a mode + other than <literal>"none"</literal> requires + you to specify a <option>passCommand</option> + or a <option>passphrase</option>. + ''; + }; + + encryption.passCommand = mkOption { + type = with types; nullOr str; + description = '' + A command which prints the passphrase to stdout. + Mutually exclusive with <option>passphrase</option>. + ''; + default = null; + example = "cat /path/to/passphrase_file"; + }; + + encryption.passphrase = mkOption { + type = with types; nullOr str; + description = '' + The passphrase the backups are encrypted with. + Mutually exclusive with <option>passCommand</option>. + If you do not want the passphrase to be stored in the + world-readable Nix store, use <option>passCommand</option>. + ''; + default = null; + }; + + compression = mkOption { + # "auto" is optional, + # compression mode must be given, + # compression level is optional + type = types.strMatching "none|(auto,)?(lz4|zstd|zlib|lzma)(,[[:digit:]]{1,2})?"; + description = '' + Compression method to use. Refer to + <command>borg help compression</command> + for all available options. + ''; + default = "lz4"; + example = "auto,lzma"; + }; + + exclude = mkOption { + type = with types; listOf str; + description = '' + Exclude paths matching any of the given patterns. See + <command>borg help patterns</command> for pattern syntax. + ''; + default = [ ]; + example = [ + "/home/*/.cache" + "/nix" + ]; + }; + + doInit = mkOption { + type = types.bool; + description = '' + Run <command>borg init</command> if the + specified <option>repo</option> does not exist. + You should set this to <literal>false</literal> + if the repository is located on an external drive + that might not always be mounted. + ''; + default = true; + }; + + appendFailedSuffix = mkOption { + type = types.bool; + description = '' + Append a <literal>.failed</literal> suffix + to the archive name, which is only removed if + <command>borg create</command> has a zero exit status. + ''; + default = true; + }; + + prune.keep = mkOption { + # Specifying e.g. `prune.keep.yearly = -1` + # means there is no limit of yearly archives to keep + # The regex is for use with e.g. --keep-within 1y + type = with types; attrsOf (either int (strMatching "[[:digit:]]+[Hdwmy]")); + description = '' + Prune a repository by deleting all archives not matching any of the + specified retention options. See <command>borg help prune</command> + for the available options. + ''; + default = { }; + example = literalExample '' + { + within = "1d"; # Keep all archives from the last day + daily = 7; + weekly = 4; + monthly = -1; # Keep at least one archive for each month + } + ''; + }; + + prune.prefix = mkOption { + type = types.str; + description = '' + Only consider archive names starting with this prefix for pruning. + By default, only archives created by this job are considered. + Use <literal>""</literal> to consider all archives. + ''; + default = config.archiveBaseName; + defaultText = "\${archiveBaseName}"; + }; + + environment = mkOption { + type = with types; attrsOf str; + description = '' + Environment variables passed to the backup script. + You can for example specify which SSH key to use. + ''; + default = { }; + example = { BORG_RSH = "ssh -i /path/to/key"; }; + }; + + preHook = mkOption { + type = types.lines; + description = '' + Shell commands to run before the backup. + This can for example be used to mount file systems. + ''; + default = ""; + example = '' + # To add excluded paths at runtime + extraCreateArgs="$extraCreateArgs --exclude /some/path" + ''; + }; + + postInit = mkOption { + type = types.lines; + description = '' + Shell commands to run after <command>borg init</command>. + ''; + default = ""; + }; + + postCreate = mkOption { + type = types.lines; + description = '' + Shell commands to run after <command>borg create</command>. The name + of the created archive is stored in <literal>$archiveName</literal>. + ''; + default = ""; + }; + + postPrune = mkOption { + type = types.lines; + description = '' + Shell commands to run after <command>borg prune</command>. + ''; + default = ""; + }; + + postHook = mkOption { + type = types.lines; + description = '' + Shell commands to run just before exit. They are executed + even if a previous command exits with a non-zero exit code. + The latter is available as <literal>$exitStatus</literal>. + ''; + default = ""; + }; + + extraInitArgs = mkOption { + type = types.str; + description = '' + Additional arguments for <command>borg init</command>. + Can also be set at runtime using <literal>$extraInitArgs</literal>. + ''; + default = ""; + example = "--append-only"; + }; + + extraCreateArgs = mkOption { + type = types.str; + description = '' + Additional arguments for <command>borg create</command>. + Can also be set at runtime using <literal>$extraCreateArgs</literal>. + ''; + default = ""; + example = "--stats --checkpoint-interval 600"; + }; + + extraPruneArgs = mkOption { + type = types.str; + description = '' + Additional arguments for <command>borg prune</command>. + Can also be set at runtime using <literal>$extraPruneArgs</literal>. + ''; + default = ""; + example = "--save-space"; + }; + + }; + } + )); + }; + + options.services.borgbackup.repos = mkOption { + description = '' + Serve BorgBackup repositories to given public SSH keys, + restricting their access to the repository only. + Also, clients do not need to specify the absolute path when accessing the repository, + i.e. <literal>user@machine:.</literal> is enough. (Note colon and dot.) + ''; + default = { }; + type = types.attrsOf (types.submodule ( + { name, config, ... }: { + options = { + + path = mkOption { + type = types.path; + description = '' + Where to store the backups. Note that the directory + is created automatically, with correct permissions. + ''; + default = "/var/lib/borgbackup"; + }; + + user = mkOption { + type = types.str; + description = '' + The user <command>borg serve</command> is run as. + User or group needs write permission + for the specified <option>path</option>. + ''; + default = "borg"; + }; + + group = mkOption { + type = types.str; + description = '' + The group <command>borg serve</command> is run as. + User or group needs write permission + for the specified <option>path</option>. + ''; + default = "borg"; + }; + + authorizedKeys = mkOption { + type = with types; listOf str; + description = '' + Public SSH keys that are given full write access to this repository. + You should use a different SSH key for each repository you write to, because + the specified keys are restricted to running <command>borg serve</command> + and can only access this single repository. + ''; + default = [ ]; + }; + + authorizedKeysAppendOnly = mkOption { + type = with types; listOf str; + description = '' + Public SSH keys that can only be used to append new data (archives) to the repository. + Note that archives can still be marked as deleted and are subsequently removed from disk + upon accessing the repo with full write access, e.g. when pruning. + ''; + default = [ ]; + }; + + allowSubRepos = mkOption { + type = types.bool; + description = '' + Allow clients to create repositories in subdirectories of the + specified <option>path</option>. These can be accessed using + <literal>user@machine:path/to/subrepo</literal>. Note that a + <option>quota</option> applies to repositories independently. + Therefore, if this is enabled, clients can create multiple + repositories and upload an arbitrary amount of data. + ''; + default = false; + }; + + quota = mkOption { + # See the definition of parse_file_size() in src/borg/helpers/parseformat.py + type = with types; nullOr (strMatching "[[:digit:].]+[KMGTP]?"); + description = '' + Storage quota for the repository. This quota is ensured for all + sub-repositories if <option>allowSubRepos</option> is enabled + but not for the overall storage space used. + ''; + default = null; + example = "100G"; + }; + + }; + } + )); + }; + + ###### implementation + + config = mkIf (with config.services.borgbackup; jobs != { } || repos != { }) + (with config.services.borgbackup; { + assertions = + mapAttrsToList mkPassAssertion jobs + ++ mapAttrsToList mkKeysAssertion repos; + + system.activationScripts = mapAttrs' mkActivationScript jobs; + + systemd.services = + # A job named "foo" is mapped to systemd.services.borgbackup-job-foo + mapAttrs' mkBackupService jobs + # A repo named "foo" is mapped to systemd.services.borgbackup-repo-foo + // mapAttrs' mkRepoService repos; + + users = mkMerge (mapAttrsToList mkUsersConfig repos); + + environment.systemPackages = with pkgs; [ borgbackup ]; + }); +} diff --git a/nixos/modules/services/backup/duplicati.nix b/nixos/modules/services/backup/duplicati.nix new file mode 100644 index 000000000000..9772ca4d20a7 --- /dev/null +++ b/nixos/modules/services/backup/duplicati.nix @@ -0,0 +1,40 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let + cfg = config.services.duplicati; +in +{ + options = { + services.duplicati = { + enable = mkEnableOption "Duplicati"; + }; + }; + + config = mkIf cfg.enable { + environment.systemPackages = [ pkgs.duplicati ]; + + systemd.services.duplicati = { + description = "Duplicati backup"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + User = "duplicati"; + Group = "duplicati"; + ExecStart = "${pkgs.duplicati}/bin/duplicati-server --webservice-interface=any --webservice-port=8200 --server-datafolder=/var/lib/duplicati"; + Restart = "on-failure"; + }; + }; + + users.extraUsers.duplicati = { + uid = config.ids.uids.duplicati; + home = "/var/lib/duplicati"; + createHome = true; + group = "duplicati"; + }; + users.extraGroups.duplicati.gid = config.ids.gids.duplicati; + + }; +} + diff --git a/nixos/modules/services/backup/restic.nix b/nixos/modules/services/backup/restic.nix new file mode 100644 index 000000000000..21d82469c605 --- /dev/null +++ b/nixos/modules/services/backup/restic.nix @@ -0,0 +1,150 @@ +{ config, lib, pkgs, ... }: + +with lib; +{ + options.services.restic.backups = mkOption { + description = '' + Periodic backups to create with Restic. + ''; + type = types.attrsOf (types.submodule ({ name, config, ... }: { + options = { + passwordFile = mkOption { + type = types.str; + description = '' + Read the repository password from a file. + ''; + example = "/etc/nixos/restic-password"; + + }; + + repository = mkOption { + type = types.str; + description = '' + repository to backup to. + ''; + example = "sftp:backup@192.168.1.100:/backups/${name}"; + }; + + paths = mkOption { + type = types.listOf types.str; + default = []; + description = '' + Which paths to backup. + ''; + example = [ + "/var/lib/postgresql" + "/home/user/backup" + ]; + }; + + timerConfig = mkOption { + type = types.attrsOf types.str; + default = { + OnCalendar = "daily"; + }; + description = '' + When to run the backup. See man systemd.timer for details. + ''; + example = { + OnCalendar = "00:05"; + RandomizedDelaySec = "5h"; + }; + }; + + user = mkOption { + type = types.str; + default = "root"; + description = '' + As which user the backup should run. + ''; + example = "postgresql"; + }; + + extraBackupArgs = mkOption { + type = types.listOf types.str; + default = []; + description = '' + Extra arguments passed to restic backup. + ''; + example = [ + "--exclude-file=/etc/nixos/restic-ignore" + ]; + }; + + extraOptions = mkOption { + type = types.listOf types.str; + default = []; + description = '' + Extra extended options to be passed to the restic --option flag. + ''; + example = [ + "sftp.command='ssh backup@192.168.1.100 -i /home/user/.ssh/id_rsa -s sftp'" + ]; + }; + + initialize = mkOption { + type = types.bool; + default = false; + description = '' + Create the repository if it doesn't exist. + ''; + }; + }; + })); + default = {}; + example = { + localbackup = { + paths = [ "/home" ]; + repository = "/mnt/backup-hdd"; + passwordFile = "/etc/nixos/secrets/restic-password"; + initialize = true; + }; + remotebackup = { + paths = [ "/home" ]; + repository = "sftp:backup@host:/backups/home"; + passwordFile = "/etc/nixos/secrets/restic-password"; + extraOptions = [ + "sftp.command='ssh backup@host -i /etc/nixos/secrets/backup-private-key -s sftp'" + ]; + timerConfig = { + OnCalendar = "00:05"; + RandomizedDelaySec = "5h"; + }; + }; + }; + }; + + config = { + systemd.services = + mapAttrs' (name: backup: + let + extraOptions = concatMapStrings (arg: " -o ${arg}") backup.extraOptions; + connectTo = elemAt (splitString ":" backup.repository) 1; + resticCmd = "${pkgs.restic}/bin/restic${extraOptions}"; + in nameValuePair "restic-backups-${name}" ({ + environment = { + RESTIC_PASSWORD_FILE = backup.passwordFile; + RESTIC_REPOSITORY = backup.repository; + }; + path = with pkgs; [ + openssh + ]; + restartIfChanged = false; + serviceConfig = { + Type = "oneshot"; + ExecStart = "${resticCmd} backup ${concatStringsSep " " backup.extraBackupArgs} ${concatStringsSep " " backup.paths}"; + User = backup.user; + }; + } // optionalAttrs backup.initialize { + preStart = '' + ${resticCmd} snapshots || ${resticCmd} init + ''; + }) + ) config.services.restic.backups; + systemd.timers = + mapAttrs' (name: backup: nameValuePair "restic-backups-${name}" { + wantedBy = [ "timers.target" ]; + timerConfig = backup.timerConfig; + }) config.services.restic.backups; + }; +} diff --git a/nixos/modules/services/backup/tarsnap.nix b/nixos/modules/services/backup/tarsnap.nix index 59e9d122fb50..4fc7c24813a5 100644 --- a/nixos/modules/services/backup/tarsnap.nix +++ b/nixos/modules/services/backup/tarsnap.nix @@ -238,6 +238,20 @@ in Whether to produce verbose logging output. ''; }; + explicitSymlinks = mkOption { + type = types.bool; + default = false; + description = '' + Whether to follow symlinks specified as archives. + ''; + }; + followSymlinks = mkOption { + type = types.bool; + default = false; + description = '' + Whether to follow all symlinks in archive trees. + ''; + }; }; } )); @@ -285,12 +299,12 @@ in }) gcfg.archives); systemd.services = - mapAttrs' (name: cfg: nameValuePair "tarsnap-${name}" { + (mapAttrs' (name: cfg: nameValuePair "tarsnap-${name}" { description = "Tarsnap archive '${name}'"; requires = [ "network-online.target" ]; after = [ "network-online.target" ]; - path = [ pkgs.iputils pkgs.tarsnap pkgs.utillinux ]; + path = with pkgs; [ iputils tarsnap utillinux ]; # In order for the persistent tarsnap timer to work reliably, we have to # make sure that the tarsnap server is reachable after systemd starts up @@ -300,10 +314,12 @@ in while ! ping -q -c 1 v1-0-0-server.tarsnap.com &> /dev/null; do sleep 3; done ''; - script = - let run = ''tarsnap --configfile "/etc/tarsnap/${name}.conf" \ - -c -f "${name}-$(date +"%Y%m%d%H%M%S")" \ + script = let + tarsnap = ''tarsnap --configfile "/etc/tarsnap/${name}.conf"''; + run = ''${tarsnap} -c -f "${name}-$(date +"%Y%m%d%H%M%S")" \ ${optionalString cfg.verbose "-v"} \ + ${optionalString cfg.explicitSymlinks "-H"} \ + ${optionalString cfg.followSymlinks "-L"} \ ${concatStringsSep " " cfg.directories}''; in if (cfg.cachedir != null) then '' mkdir -p ${cfg.cachedir} @@ -313,7 +329,7 @@ in if [ ! -e ${cfg.cachedir}/firstrun ]; then ( flock 10 flock -u 9 - tarsnap --configfile "/etc/tarsnap/${name}.conf" --fsck + ${tarsnap} --fsck flock 9 ) 10>${cfg.cachedir}/firstrun fi @@ -329,7 +345,44 @@ in CapabilityBoundingSet = [ "CAP_DAC_READ_SEARCH" ]; PermissionsStartOnly = "true"; }; - }) gcfg.archives; + }) gcfg.archives) // + + (mapAttrs' (name: cfg: nameValuePair "tarsnap-restore-${name}"{ + description = "Tarsnap restore '${name}'"; + requires = [ "network-online.target" ]; + + path = with pkgs; [ iputils tarsnap utillinux ]; + + script = let + tarsnap = ''tarsnap --configfile "/etc/tarsnap/${name}.conf"''; + lastArchive = ''$(${tarsnap} --list-archives | sort | tail -1)''; + run = ''${tarsnap} -x -f "${lastArchive}" ${optionalString cfg.verbose "-v"}''; + + in if (cfg.cachedir != null) then '' + mkdir -p ${cfg.cachedir} + chmod 0700 ${cfg.cachedir} + + ( flock 9 + if [ ! -e ${cfg.cachedir}/firstrun ]; then + ( flock 10 + flock -u 9 + ${tarsnap} --fsck + flock 9 + ) 10>${cfg.cachedir}/firstrun + fi + ) 9>${cfg.cachedir}/lockf + + exec flock ${cfg.cachedir}/firstrun ${run} + '' else "exec ${run}"; + + serviceConfig = { + Type = "oneshot"; + IOSchedulingClass = "idle"; + NoNewPrivileges = "true"; + CapabilityBoundingSet = [ "CAP_DAC_READ_SEARCH" ]; + PermissionsStartOnly = "true"; + }; + }) gcfg.archives); # Note: the timer must be Persistent=true, so that systemd will start it even # if e.g. your laptop was asleep while the latest interval occurred. diff --git a/nixos/modules/services/backup/znapzend.nix b/nixos/modules/services/backup/znapzend.nix index 762bb4b38675..3d133f82d204 100644 --- a/nixos/modules/services/backup/znapzend.nix +++ b/nixos/modules/services/backup/znapzend.nix @@ -386,7 +386,7 @@ in echo Resetting znapzend zetups ${pkgs.znapzend}/bin/znapzendzetup list \ | grep -oP '(?<=\*\*\* backup plan: ).*(?= \*\*\*)' \ - | xargs ${pkgs.znapzend}/bin/znapzendzetup delete + | xargs -I{} ${pkgs.znapzend}/bin/znapzendzetup delete "{}" '' + concatStringsSep "\n" (mapAttrsToList (dataset: config: '' echo Importing znapzend zetup ${config} for dataset ${dataset} ${pkgs.znapzend}/bin/znapzendzetup import --write ${dataset} ${config} diff --git a/nixos/modules/services/cluster/kubernetes/default.nix b/nixos/modules/services/cluster/kubernetes/default.nix index 4a2c6f0833eb..aeb0a0d2432d 100644 --- a/nixos/modules/services/cluster/kubernetes/default.nix +++ b/nixos/modules/services/cluster/kubernetes/default.nix @@ -279,7 +279,7 @@ in { tokenAuthFile = mkOption { description = '' Kubernetes apiserver token authentication file. See - <link xlink:href="http://kubernetes.io/docs/admin/authentication.html"/> + <link xlink:href="https://kubernetes.io/docs/admin/authentication.html"/> ''; default = null; type = types.nullOr types.path; @@ -288,7 +288,7 @@ in { basicAuthFile = mkOption { description = '' Kubernetes apiserver basic authentication file. See - <link xlink:href="http://kubernetes.io/docs/admin/authentication.html"/> + <link xlink:href="https://kubernetes.io/docs/admin/authentication.html"/> ''; default = pkgs.writeText "users" '' kubernetes,admin,0 @@ -299,7 +299,7 @@ in { authorizationMode = mkOption { description = '' Kubernetes apiserver authorization mode (AlwaysAllow/AlwaysDeny/ABAC/RBAC). See - <link xlink:href="http://kubernetes.io/docs/admin/authorization.html"/> + <link xlink:href="https://kubernetes.io/docs/admin/authorization.html"/> ''; default = ["RBAC" "Node"]; type = types.listOf (types.enum ["AlwaysAllow" "AlwaysDeny" "ABAC" "RBAC" "Node"]); @@ -308,7 +308,7 @@ in { authorizationPolicy = mkOption { description = '' Kubernetes apiserver authorization policy file. See - <link xlink:href="http://kubernetes.io/docs/admin/authorization.html"/> + <link xlink:href="https://kubernetes.io/docs/admin/authorization.html"/> ''; default = []; type = types.listOf types.attrs; @@ -332,7 +332,7 @@ in { runtimeConfig = mkOption { description = '' Api runtime configuration. See - <link xlink:href="http://kubernetes.io/docs/admin/cluster-management.html"/> + <link xlink:href="https://kubernetes.io/docs/admin/cluster-management.html"/> ''; default = "authentication.k8s.io/v1beta1=true"; example = "api/all=false,api/v1=true"; @@ -342,7 +342,7 @@ in { admissionControl = mkOption { description = '' Kubernetes admission control plugins to use. See - <link xlink:href="http://kubernetes.io/docs/admin/admission-controllers/"/> + <link xlink:href="https://kubernetes.io/docs/admin/admission-controllers/"/> ''; default = ["NamespaceLifecycle" "LimitRanger" "ServiceAccount" "ResourceQuota" "DefaultStorageClass" "DefaultTolerationSeconds" "NodeRestriction"]; example = [ @@ -766,7 +766,7 @@ in { rm /opt/cni/bin/* || true ${concatMapStrings (package: '' echo "Linking cni package: ${package}" - ln -fs ${package.plugins}/* /opt/cni/bin + ln -fs ${package}/bin/* /opt/cni/bin '') cfg.kubelet.cni.packages} ''; serviceConfig = { @@ -828,7 +828,7 @@ in { }; # Allways include cni plugins - services.kubernetes.kubelet.cni.packages = [pkgs.cni]; + services.kubernetes.kubelet.cni.packages = [pkgs.cni-plugins]; boot.kernelModules = ["br_netfilter"]; diff --git a/nixos/modules/services/computing/boinc/client.nix b/nixos/modules/services/computing/boinc/client.nix index e43b6bbb2536..8abe3c5b8c9b 100644 --- a/nixos/modules/services/computing/boinc/client.nix +++ b/nixos/modules/services/computing/boinc/client.nix @@ -6,6 +6,13 @@ let cfg = config.services.boinc; allowRemoteGuiRpcFlag = optionalString cfg.allowRemoteGuiRpc "--allow_remote_gui_rpc"; + fhsEnv = pkgs.buildFHSUserEnv { + name = "boinc-fhs-env"; + targetPkgs = pkgs': [ cfg.package ] ++ cfg.extraEnvPackages; + runScript = "/bin/boinc_client"; + }; + fhsEnvExecutable = "${fhsEnv}/bin/${fhsEnv.name}"; + in { options.services.boinc = { @@ -49,6 +56,43 @@ in See also: <link xlink:href="http://boinc.berkeley.edu/wiki/Controlling_BOINC_remotely#Remote_access"/> ''; }; + + extraEnvPackages = mkOption { + type = types.listOf types.package; + default = []; + example = "[ pkgs.virtualbox ]"; + description = '' + Additional packages to make available in the environment in which + BOINC will run. Common choices are: + <variablelist> + <varlistentry> + <term><varname>pkgs.virtualbox</varname></term> + <listitem><para> + The VirtualBox virtual machine framework. Required by some BOINC + projects, such as ATLAS@home. + </para></listitem> + </varlistentry> + <varlistentry> + <term><varname>pkgs.ocl-icd</varname></term> + <listitem><para> + OpenCL infrastructure library. Required by BOINC projects that + use OpenCL, in addition to a device-specific OpenCL driver. + </para></listitem> + </varlistentry> + <varlistentry> + <term><varname>pkgs.linuxPackages.nvidia_x11</varname></term> + <listitem><para> + Provides CUDA libraries. Required by BOINC projects that use + CUDA. Note that this requires an NVIDIA graphics device to be + present on the system. + </para><para> + Also provides OpenCL drivers for NVIDIA GPUs; + <varname>pkgs.ocl-icd</varname> is also needed in this case. + </para></listitem> + </varlistentry> + </variablelist> + ''; + }; }; config = mkIf cfg.enable { @@ -70,7 +114,7 @@ in chown boinc ${cfg.dataDir} ''; script = '' - ${cfg.package}/bin/boinc_client --dir ${cfg.dataDir} --redirectio ${allowRemoteGuiRpcFlag} + ${fhsEnvExecutable} --dir ${cfg.dataDir} --redirectio ${allowRemoteGuiRpcFlag} ''; serviceConfig = { PermissionsStartOnly = true; # preStart must be run as root diff --git a/nixos/modules/services/continuous-integration/buildkite-agent.nix b/nixos/modules/services/continuous-integration/buildkite-agent.nix index 0a0c9f665d25..d647b7b9fa49 100644 --- a/nixos/modules/services/continuous-integration/buildkite-agent.nix +++ b/nixos/modules/services/continuous-integration/buildkite-agent.nix @@ -17,8 +17,8 @@ let hooksDir = let mkHookEntry = name: value: '' - cat > $out/${name} <<EOF - #! ${pkgs.stdenv.shell} + cat > $out/${name} <<'EOF' + #! ${pkgs.runtimeShell} set -e ${value} EOF diff --git a/nixos/modules/services/continuous-integration/jenkins/default.nix b/nixos/modules/services/continuous-integration/jenkins/default.nix index 54047a50caa6..c2f4e9c0c5a7 100644 --- a/nixos/modules/services/continuous-integration/jenkins/default.nix +++ b/nixos/modules/services/continuous-integration/jenkins/default.nix @@ -145,6 +145,11 @@ in { }; config = mkIf cfg.enable { + # server references the dejavu fonts + environment.systemPackages = [ + pkgs.dejavu_fonts + ]; + users.extraGroups = optional (cfg.group == "jenkins") { name = "jenkins"; gid = config.ids.gids.jenkins; @@ -200,10 +205,12 @@ in { ${replacePlugins} ''; + # For reference: https://wiki.jenkins.io/display/JENKINS/JenkinsLinuxStartupScript script = '' ${pkgs.jdk}/bin/java ${concatStringsSep " " cfg.extraJavaOptions} -jar ${cfg.package}/webapps/jenkins.war --httpListenAddress=${cfg.listenAddress} \ --httpPort=${toString cfg.port} \ --prefix=${cfg.prefix} \ + -Djava.awt.headless=true \ ${concatStringsSep " " cfg.extraOptions} ''; diff --git a/nixos/modules/services/databases/4store-endpoint.nix b/nixos/modules/services/databases/4store-endpoint.nix index 906cb320df98..d528355671f6 100644 --- a/nixos/modules/services/databases/4store-endpoint.nix +++ b/nixos/modules/services/databases/4store-endpoint.nix @@ -2,7 +2,7 @@ let cfg = config.services.fourStoreEndpoint; endpointUser = "fourstorehttp"; - run = "${pkgs.su}/bin/su -s ${pkgs.stdenv.shell} ${endpointUser} -c"; + run = "${pkgs.su}/bin/su -s ${pkgs.runtimeShell} ${endpointUser} -c"; in with lib; { diff --git a/nixos/modules/services/databases/4store.nix b/nixos/modules/services/databases/4store.nix index 62856822f906..abb62e1f2637 100644 --- a/nixos/modules/services/databases/4store.nix +++ b/nixos/modules/services/databases/4store.nix @@ -3,7 +3,7 @@ let cfg = config.services.fourStore; stateDir = "/var/lib/4store"; fourStoreUser = "fourstore"; - run = "${pkgs.su}/bin/su -s ${pkgs.stdenv.shell} ${fourStoreUser}"; + run = "${pkgs.su}/bin/su -s ${pkgs.runtimeShell} ${fourStoreUser}"; in with lib; { diff --git a/nixos/modules/services/databases/foundationdb.nix b/nixos/modules/services/databases/foundationdb.nix new file mode 100644 index 000000000000..22acddc8ca91 --- /dev/null +++ b/nixos/modules/services/databases/foundationdb.nix @@ -0,0 +1,360 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.foundationdb; + + # used for initial cluster configuration + initialIpAddr = if (cfg.publicAddress != "auto") then cfg.publicAddress else "127.0.0.1"; + + fdbServers = n: + concatStringsSep "\n" (map (x: "[fdbserver.${toString (x+cfg.listenPortStart)}]") (range 0 (n - 1))); + + backupAgents = n: + concatStringsSep "\n" (map (x: "[backup_agent.${toString x}]") (range 1 n)); + + configFile = pkgs.writeText "foundationdb.conf" '' + [general] + cluster_file = /etc/foundationdb/fdb.cluster + + [fdbmonitor] + restart_delay = ${toString cfg.restartDelay} + user = ${cfg.user} + group = ${cfg.group} + + [fdbserver] + command = ${pkgs.foundationdb}/bin/fdbserver + public_address = ${cfg.publicAddress}:$ID + listen_address = ${cfg.listenAddress} + datadir = ${cfg.dataDir}/$ID + logdir = ${cfg.logDir} + logsize = ${cfg.logSize} + maxlogssize = ${cfg.maxLogSize} + ${optionalString (cfg.class != null) "class = ${cfg.class}"} + memory = ${cfg.memory} + storage_memory = ${cfg.storageMemory} + + ${optionalString (cfg.locality.machineId != null) "locality_machineid=${cfg.locality.machineId}"} + ${optionalString (cfg.locality.zoneId != null) "locality_zoneid=${cfg.locality.zoneId}"} + ${optionalString (cfg.locality.datacenterId != null) "locality_dcid=${cfg.locality.datacenterId}"} + ${optionalString (cfg.locality.dataHall != null) "locality_data_hall=${cfg.locality.dataHall}"} + + ${fdbServers cfg.serverProcesses} + + [backup_agent] + command = ${pkgs.foundationdb}/libexec/backup_agent + ${backupAgents cfg.backupProcesses} + ''; +in +{ + options.services.foundationdb = { + + enable = mkEnableOption "FoundationDB Server"; + + publicAddress = mkOption { + type = types.str; + default = "auto"; + description = "Publicly visible IP address of the process. Port is determined by process ID"; + }; + + listenAddress = mkOption { + type = types.str; + default = "public"; + description = "Publicly visible IP address of the process. Port is determined by process ID"; + }; + + listenPortStart = mkOption { + type = types.int; + default = 4500; + description = '' + Starting port number for database listening sockets. Every FDB process binds to a + subsequent port, to this number reflects the start of the overall range. e.g. having + 8 server processes will use all ports between 4500 and 4507. + ''; + }; + + openFirewall = mkOption { + type = types.bool; + default = false; + description = '' + Open the firewall ports corresponding to FoundationDB processes and coordinators + using <option>config.networking.firewall.*</option>. + ''; + }; + + dataDir = mkOption { + type = types.path; + default = "/var/lib/foundationdb"; + description = "Data directory. All cluster data will be put under here."; + }; + + logDir = mkOption { + type = types.path; + default = "/var/log/foundationdb"; + description = "Log directory."; + }; + + user = mkOption { + type = types.str; + default = "foundationdb"; + description = "User account under which FoundationDB runs."; + }; + + group = mkOption { + type = types.str; + default = "foundationdb"; + description = "Group account under which FoundationDB runs."; + }; + + class = mkOption { + type = types.nullOr (types.enum [ "storage" "transaction" "stateless" ]); + default = null; + description = "Process class"; + }; + + restartDelay = mkOption { + type = types.int; + default = 10; + description = "Number of seconds to wait before restarting servers."; + }; + + logSize = mkOption { + type = types.string; + default = "10MiB"; + description = '' + Roll over to a new log file after the current log file + reaches the specified size. + ''; + }; + + maxLogSize = mkOption { + type = types.string; + default = "100MiB"; + description = '' + Delete the oldest log file when the total size of all log + files exceeds the specified size. If set to 0, old log files + will not be deleted. + ''; + }; + + serverProcesses = mkOption { + type = types.int; + default = 1; + description = "Number of fdbserver processes to run."; + }; + + backupProcesses = mkOption { + type = types.int; + default = 1; + description = "Number of backup_agent processes to run for snapshots."; + }; + + memory = mkOption { + type = types.string; + default = "8GiB"; + description = '' + Maximum memory used by the process. The default value is + <literal>8GiB</literal>. When specified without a unit, + <literal>MiB</literal> is assumed. This parameter does not + change the memory allocation of the program. Rather, it sets + a hard limit beyond which the process will kill itself and + be restarted. The default value of <literal>8GiB</literal> + is double the intended memory usage in the default + configuration (providing an emergency buffer to deal with + memory leaks or similar problems). It is not recommended to + decrease the value of this parameter below its default + value. It may be increased if you wish to allocate a very + large amount of storage engine memory or cache. In + particular, when the <literal>storageMemory</literal> + parameter is increased, the <literal>memory</literal> + parameter should be increased by an equal amount. + ''; + }; + + storageMemory = mkOption { + type = types.string; + default = "1GiB"; + description = '' + Maximum memory used for data storage. The default value is + <literal>1GiB</literal>. When specified without a unit, + <literal>MB</literal> is assumed. Clusters using the memory + storage engine will be restricted to using this amount of + memory per process for purposes of data storage. Memory + overhead associated with storing the data is counted against + this total. If you increase the + <literal>storageMemory</literal>, you should also increase + the <literal>memory</literal> parameter by the same amount. + ''; + }; + + locality = mkOption { + default = { + machineId = null; + zoneId = null; + datacenterId = null; + dataHall = null; + }; + + description = '' + FoundationDB locality settings. + ''; + + type = types.submodule ({ + options = { + machineId = mkOption { + default = null; + type = types.nullOr types.str; + description = '' + Machine identifier key. All processes on a machine should share a + unique id. By default, processes on a machine determine a unique id to share. + This does not generally need to be set. + ''; + }; + + zoneId = mkOption { + default = null; + type = types.nullOr types.str; + description = '' + Zone identifier key. Processes that share a zone id are + considered non-unique for the purposes of data replication. + If unset, defaults to machine id. + ''; + }; + + datacenterId = mkOption { + default = null; + type = types.nullOr types.str; + description = '' + Data center identifier key. All processes physically located in a + data center should share the id. If you are depending on data + center based replication this must be set on all processes. + ''; + }; + + dataHall = mkOption { + default = null; + type = types.nullOr types.str; + description = '' + Data hall identifier key. All processes physically located in a + data hall should share the id. If you are depending on data + hall based replication this must be set on all processes. + ''; + }; + }; + }); + }; + + extraReadWritePaths = mkOption { + default = [ ]; + type = types.listOf types.path; + description = '' + An extra set of filesystem paths that FoundationDB can read to + and write from. By default, FoundationDB runs under a heavily + namespaced systemd environment without write access to most of + the filesystem outside of its data and log directories. By + adding paths to this list, the set of writeable paths will be + expanded. This is useful for allowing e.g. backups to local files, + which must be performed on behalf of the foundationdb service. + ''; + }; + + pidfile = mkOption { + type = types.path; + default = "/run/foundationdb.pid"; + description = "Path to pidfile for fdbmonitor."; + }; + }; + + config = mkIf cfg.enable { + meta.doc = ./foundationdb.xml; + meta.maintainers = with lib.maintainers; [ thoughtpolice ]; + + environment.systemPackages = [ pkgs.foundationdb ]; + + users.extraUsers = optionalAttrs (cfg.user == "foundationdb") (singleton + { name = "foundationdb"; + description = "FoundationDB User"; + uid = config.ids.uids.foundationdb; + group = cfg.group; + }); + + users.extraGroups = optionalAttrs (cfg.group == "foundationdb") (singleton + { name = "foundationdb"; + gid = config.ids.gids.foundationdb; + }); + + networking.firewall.allowedTCPPortRanges = mkIf cfg.openFirewall + [ { from = cfg.listenPortStart; + to = (cfg.listenPortStart + cfg.serverProcesses) - 1; + } + ]; + + systemd.services.foundationdb = { + description = "FoundationDB Service"; + + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + unitConfig = + { RequiresMountsFor = "${cfg.dataDir} ${cfg.logDir}"; + }; + + serviceConfig = + let rwpaths = [ cfg.dataDir cfg.logDir cfg.pidfile "/etc/foundationdb" ] + ++ cfg.extraReadWritePaths; + in + { Type = "simple"; + Restart = "always"; + RestartSec = 5; + User = cfg.user; + Group = cfg.group; + PIDFile = "${cfg.pidfile}"; + + PermissionsStartOnly = true; # setup needs root perms + TimeoutSec = 120; # give reasonable time to shut down + + # Security options + NoNewPrivileges = true; + ProtectHome = true; + ProtectSystem = "strict"; + ProtectKernelTunables = true; + ProtectControlGroups = true; + PrivateTmp = true; + PrivateDevices = true; + ReadWritePaths = lib.concatStringsSep " " (map (x: "-" + x) rwpaths); + }; + + path = [ pkgs.foundationdb pkgs.coreutils ]; + + preStart = '' + rm -f ${cfg.pidfile} && \ + touch ${cfg.pidfile} && \ + chown -R ${cfg.user}:${cfg.group} ${cfg.pidfile} + + for x in "${cfg.logDir}" "${cfg.dataDir}" /etc/foundationdb; do + [ ! -d "$x" ] && mkdir -m 0700 -vp "$x" && chown -R ${cfg.user}:${cfg.group} "$x"; + done + + if [ ! -f /etc/foundationdb/fdb.cluster ]; then + cf=/etc/foundationdb/fdb.cluster + desc=$(tr -dc A-Za-z0-9 </dev/urandom 2>/dev/null | head -c8) + rand=$(tr -dc A-Za-z0-9 </dev/urandom 2>/dev/null | head -c8) + echo ''${desc}:''${rand}@${initialIpAddr}:${builtins.toString cfg.listenPortStart} > $cf + chmod 0660 $cf && chown -R ${cfg.user}:${cfg.group} $cf + touch "${cfg.dataDir}/.first_startup" + fi + ''; + + script = '' + exec fdbmonitor --lockfile ${cfg.pidfile} --conffile ${configFile}; + ''; + + postStart = '' + if [ -e "${cfg.dataDir}/.first_startup" ]; then + fdbcli --exec "configure new single ssd" + rm -f "${cfg.dataDir}/.first_startup"; + fi + ''; + }; + }; +} diff --git a/nixos/modules/services/databases/foundationdb.xml b/nixos/modules/services/databases/foundationdb.xml new file mode 100644 index 000000000000..2a0e3c76c9d8 --- /dev/null +++ b/nixos/modules/services/databases/foundationdb.xml @@ -0,0 +1,280 @@ +<chapter xmlns="http://docbook.org/ns/docbook" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:xi="http://www.w3.org/2001/XInclude" + version="5.0" + xml:id="module-foundationdb"> + +<title>FoundationDB</title> + +<para><emphasis>Source:</emphasis> <filename>modules/services/databases/foundationdb.nix</filename></para> + +<para><emphasis>Upstream documentation:</emphasis> <link xlink:href="https://apple.github.io/foundationdb/"/></para> + +<para><emphasis>Maintainer:</emphasis> Austin Seipp</para> + +<para><emphasis>Default version:</emphasis> 5.1.x</para> + +<para>FoundationDB (or "FDB") is a distributed, open source, high performance, +transactional key-value store. It can store petabytes of data and deliver +exceptional performance while maintaining consistency and ACID semantics +(serializable transactions) over a large cluster.</para> + +<section><title>Configuring and basic setup</title> + +<para>To enable FoundationDB, add the following to your +<filename>configuration.nix</filename>: + +<programlisting> +services.foundationdb.enable = true; +</programlisting> +</para> + +<para>After running <command>nixos-rebuild</command>, you can verify whether +FoundationDB is running by executing <command>fdbcli</command> (which is added +to <option>environment.systemPackages</option>): + +<programlisting> +$ sudo -u foundationdb fdbcli +Using cluster file `/etc/foundationdb/fdb.cluster'. + +The database is available. + +Welcome to the fdbcli. For help, type `help'. +fdb> status + +Using cluster file `/etc/foundationdb/fdb.cluster'. + +Configuration: + Redundancy mode - single + Storage engine - memory + Coordinators - 1 + +Cluster: + FoundationDB processes - 1 + Machines - 1 + Memory availability - 5.4 GB per process on machine with least available + Fault Tolerance - 0 machines + Server time - 04/20/18 15:21:14 + +... + +fdb> +</programlisting> +</para> + +<para>FoundationDB is run under the <command>foundationdb</command> user and +group by default, but this may be changed in the NixOS configuration. The +systemd unit <command>foundationdb.service</command> controls the +<command>fdbmonitor</command> process.</para> + +<para>By default, the NixOS module for FoundationDB creates a single +SSD-storage based database for development and basic usage. This storage engine +is designed for SSDs and will perform poorly on HDDs; however it can handle far +more data than the alternative "memory" engine and is a better default choice +for most deployments. (Note that you can change the storage backend on-the-fly +for a given FoundationDB cluster using <command>fdbcli</command>.)</para> + +<para>Furthermore, only 1 server process and 1 backup agent are started in the +default configuration. See below for more on scaling to increase this.</para> + +<para>FoundationDB stores all data for all server processes under +<filename>/var/lib/foundationdb</filename>. You can override this using +<option>services.foundationdb.dataDir</option>, e.g. + +<programlisting> +services.foundationdb.dataDir = "/data/fdb"; +</programlisting> + +</para> + +<para>Similarly, logs are stored under +<filename>/var/log/foundationdb</filename> by default, and there is a +corresponding <option>services.foundationdb.logDir</option> as well.</para> + +</section> + +<section><title>Scaling processes and backup agents</title> + +<para>Scaling the number of server processes is quite easy; simply specify +<option>services.foundationdb.serverProcesses</option> to be the number of +FoundationDB worker processes that should be started on the machine.</para> + +<para>FoundationDB worker processes typically require 4GB of RAM per-process at +minimum for good performance, so this option is set to 1 by default since the +maximum amount of RAM is unknown. You're advised to abide by this restriction, +so pick a number of processes so that each has 4GB or more.</para> + +<para>A similar option exists in order to scale backup agent processes, +<option>services.foundationdb.backupProcesses</option>. Backup agents are not +as performance/RAM sensitive, so feel free to experiment with the number of +available backup processes.</para> + +</section> + +<section><title>Clustering</title> + +<para>FoundationDB on NixOS works similarly to other Linux systems, so this +section will be brief. Please refer to the full FoundationDB documentation for +more on clustering.</para> + +<para>FoundationDB organizes clusters using a set of +<emphasis>coordinators</emphasis>, which are just specially-designated worker +processes. By default, every installation of FoundationDB on NixOS will start +as its own individual cluster, with a single coordinator: the first worker +process on <command>localhost</command>.</para> + +<para>Coordinators are specified globally using the +<command>/etc/foundationdb/fdb.cluster</command> file, which all servers and +client applications will use to find and join coordinators. Note that this file +<emphasis>can not</emphasis> be managed by NixOS so easily: FoundationDB is +designed so that it will rewrite the file at runtime for all clients and nodes +when cluster coordinators change, with clients transparently handling this +without intervention. It is fundamentally a mutable file, and you should not +try to manage it in any way in NixOS.</para> + +<para>When dealing with a cluster, there are two main things you want to +do:</para> + +<itemizedlist> + <listitem><para>Add a node to the cluster for storage/compute.</para></listitem> + <listitem><para>Promote an ordinary worker to a coordinator.</para></listitem> +</itemizedlist> + +<para>A node must already be a member of the cluster in order to properly be +promoted to a coordinator, so you must always add it first if you wish to +promote it.</para> + +<para>To add a machine to a FoundationDB cluster:</para> + +<itemizedlist> + <listitem><para>Choose one of the servers to start as the initial coordinator. + </para></listitem> + <listitem><para>Copy the <command>/etc/foundationdb/fdb.cluster</command> file + from this server to all the other servers. Restart FoundationDB on all of + these other servers, so they join the cluster.</para></listitem> + <listitem><para>All of these servers are now connected and working together + in the cluster, under the chosen coordinator.</para></listitem> +</itemizedlist> + +<para>At this point, you can add as many nodes as you want by just repeating +the above steps. By default there will still be a single coordinator: you can +use <command>fdbcli</command> to change this and add new coordinators.</para> + +<para>As a convenience, FoundationDB can automatically assign coordinators +based on the redundancy mode you wish to achieve for the cluster. Once all the +nodes have been joined, simply set the replication policy, and then issue the +<command>coordinators auto</command> command</para> + +<para>For example, assuming we have 3 nodes available, we can enable double +redundancy mode, then auto-select coordinators. For double redundancy, 3 +coordinators is ideal: therefore FoundationDB will make +<emphasis>every</emphasis> node a coordinator automatically:</para> + +<programlisting> +fdbcli> configure double ssd +fdbcli> coordinators auto +</programlisting> + +<para>This will transparently update all the servers within seconds, and +appropriately rewrite the <command>fdb.cluster</command> file, as well as +informing all client processes to do the same.</para> + +</section> + +<section><title>Client connectivity</title> + +<para>By default, all clients must use the current +<command>fdb.cluster</command> file to access a given FoundationDB cluster. +This file is located by default in +<command>/etc/foundationdb/fdb.cluster</command> on all machines with the +FoundationDB service enabled, so you may copy the active one from your cluster +to a new node in order to connect, if it is not part of the cluster.</para> + +</section> + +<section><title>Backups and Disaster Recovery</title> + +<para>The usual rules for doing FoundationDB backups apply on NixOS as written +in the FoundationDB manual. However, one important difference is the security +profile for NixOS: by default, the <command>foundationdb</command> systemd unit +uses <emphasis>Linux namespaces</emphasis> to restrict write access to the +system, except for the log directory, data directory, and the +<command>/etc/foundationdb/</command> directory. This is enforced by default +and cannot be disabled.</para> + +<para>However, a side effect of this is that the <command>fdbbackup</command> +command doesn't work properly for local filesystem backups: FoundationDB uses a +server process alongside the database processes to perform backups and copy the +backups to the filesystem. As a result, this process is put under the +restricted namespaces above: the backup process can only write to a limited +number of paths.</para> + +<para>In order to allow flexible backup locations on local disks, the +FoundationDB NixOS module supports a +<option>services.foundationdb.extraReadWritePaths</option> option. This option +takes a list of paths, and adds them to the systemd unit, allowing the +processes inside the service to write (and read) the specified +directories.</para> + +<para>For example, to create backups in <command>/opt/fdb-backups</command>, +first set up the paths in the module options:</para> + +<programlisting> +services.foundationdb.extraReadWritePaths = [ "/opt/fdb-backups" ]; +</programlisting> + +<para>Restart the FoundationDB service, and it will now be able to write to +this directory (even if it does not yet exist.) Note: this path +<emphasis>must</emphasis> exist before restarting the unit. Otherwise, systemd +will not include it in the private FoundationDB namespace (and it will not add +it dynamically at runtime).</para> + +<para>You can now perform a backup:</para> + +<programlisting> +$ sudo -u foundationdb fdbbackup start -t default -d file:///opt/fdb-backups +$ sudo -u foundationdb fdbbackup status -t default +</programlisting> + +</section> + +<section><title>Known limitations</title> + +<para>The FoundationDB setup for NixOS should currently be considered beta. +FoundationDB is not new software, but the NixOS compilation and integration has +only undergone fairly basic testing of all the available functionality.</para> + +<itemizedlist> + <listitem><para>TLS plugin support is compiled in, but it's currently not + possible to specify the set of TLS certificate options in + <command>services.foundationdb</command></para></listitem> + <listitem><para>There is no way to specify individual parameters for + individual <command>fdbserver</command> processes. Currently, all server + processes inherit all the global <command>fdbmonitor</command> settings. + </para></listitem> + <listitem><para>Python bindings are not currently installed.</para></listitem> + <listitem><para>Ruby bindings are not currently installed.</para></listitem> + <listitem><para>Java bindings are not currently installed.</para></listitem> + <listitem><para>Go bindings are not currently installed.</para></listitem> +</itemizedlist> + +</section> + +<section><title>Options</title> + +<para>NixOS's FoundationDB module allows you to configure all of the most +relevant configuration options for <command>fdbmonitor</command>, matching it +quite closely. For a complete list of all options, check <command>man +configuration.nix</command>.</para> + +</section> + +<section><title>Full documentation</title> + +<para>FoundationDB is a complex piece of software, and requires careful +administration to properly use. Full documentation for administration can be +found here: <link xlink:href="https://apple.github.io/foundationdb/"/>.</para> + +</section> + +</chapter> diff --git a/nixos/modules/services/databases/pgmanage.nix b/nixos/modules/services/databases/pgmanage.nix index 86733a3e5a07..d1b48c06440e 100644 --- a/nixos/modules/services/databases/pgmanage.nix +++ b/nixos/modules/services/databases/pgmanage.nix @@ -22,7 +22,7 @@ let web_root = ${cfg.package}/etc/pgmanage/web_root - data_root = ${cfg.dataRoot} + sql_root = ${cfg.sqlRoot} ${optionalString (!isNull cfg.tls) '' tls_cert = ${cfg.tls.cert} @@ -130,7 +130,7 @@ let ''; }; - dataRoot = mkOption { + sqlRoot = mkOption { type = types.str; default = "/var/lib/pgmanage"; description = '' @@ -210,7 +210,7 @@ in { users."${pgmanage}" = { name = pgmanage; group = pgmanage; - home = cfg.dataRoot; + home = cfg.sqlRoot; createHome = true; }; groups."${pgmanage}" = { diff --git a/nixos/modules/services/databases/postgresql.nix b/nixos/modules/services/databases/postgresql.nix index 0dcbfe2e47ac..f022e0863dfd 100644 --- a/nixos/modules/services/databases/postgresql.nix +++ b/nixos/modules/services/databases/postgresql.nix @@ -36,9 +36,6 @@ let ${cfg.extraConfig} ''; - pre84 = versionOlder (builtins.parseDrvName postgresql.name).version "8.4"; - - in { @@ -182,7 +179,7 @@ in services.postgresql.authentication = mkAfter '' # Generated file; do not edit! - local all all ident ${optionalString pre84 "sameuser"} + local all all ident host all all 127.0.0.1/32 md5 host all all ::1/128 md5 ''; diff --git a/nixos/modules/services/editors/emacs.nix b/nixos/modules/services/editors/emacs.nix index 2c5a0c4849ef..ba7ec967919e 100644 --- a/nixos/modules/services/editors/emacs.nix +++ b/nixos/modules/services/editors/emacs.nix @@ -7,7 +7,7 @@ let cfg = config.services.emacs; editorScript = pkgs.writeScriptBin "emacseditor" '' - #!${pkgs.stdenv.shell} + #!${pkgs.runtimeShell} if [ -z "$1" ]; then exec ${cfg.package}/bin/emacsclient --create-frame --alternate-editor ${cfg.package}/bin/emacs else @@ -15,6 +15,25 @@ let fi ''; +desktopApplicationFile = pkgs.writeTextFile { + name = "emacsclient.desktop"; + destination = "/share/applications/emacsclient.desktop"; + text = '' +[Desktop Entry] +Name=Emacsclient +GenericName=Text Editor +Comment=Edit text +MimeType=text/english;text/plain;text/x-makefile;text/x-c++hdr;text/x-c++src;text/x-chdr;text/x-csrc;text/x-java;text/x-moc;text/x-pascal;text/x-tcl;text/x-tex;application/x-shellscript;text/x-c;text/x-c++; +Exec=emacseditor %F +Icon=emacs +Type=Application +Terminal=false +Categories=Development;TextEditor; +StartupWMClass=Emacs +Keywords=Text;Editor; +''; +}; + in { options.services.emacs = { @@ -74,7 +93,7 @@ in { }; } // optionalAttrs cfg.enable { wantedBy = [ "default.target" ]; }; - environment.systemPackages = [ cfg.package editorScript ]; + environment.systemPackages = [ cfg.package editorScript desktopApplicationFile ]; environment.variables = { # This is required so that GTK applications launched from Emacs diff --git a/nixos/modules/services/hardware/bluetooth.nix b/nixos/modules/services/hardware/bluetooth.nix index 4a8cd86b0b11..d7ca8a431794 100644 --- a/nixos/modules/services/hardware/bluetooth.nix +++ b/nixos/modules/services/hardware/bluetooth.nix @@ -3,8 +3,8 @@ with lib; let - bluez-bluetooth = pkgs.bluez; cfg = config.hardware.bluetooth; + bluez-bluetooth = cfg.package; in { @@ -21,6 +21,16 @@ in { description = "Whether to power up the default Bluetooth controller on boot."; }; + package = mkOption { + type = types.package; + default = pkgs.bluez; + defaultText = "pkgs.bluez"; + example = "pkgs.bluez.override { enableMidi = true; }"; + description = '' + Which BlueZ package to use. + ''; + }; + extraConfig = mkOption { type = types.lines; default = ""; diff --git a/nixos/modules/services/hardware/lcd.nix b/nixos/modules/services/hardware/lcd.nix new file mode 100644 index 000000000000..d78d742cd318 --- /dev/null +++ b/nixos/modules/services/hardware/lcd.nix @@ -0,0 +1,172 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.services.hardware.lcd; + pkg = lib.getBin pkgs.lcdproc; + + serverCfg = pkgs.writeText "lcdd.conf" '' + [server] + DriverPath=${pkg}/lib/lcdproc/ + ReportToSyslog=false + Bind=${cfg.serverHost} + Port=${toString cfg.serverPort} + ${cfg.server.extraConfig} + ''; + + clientCfg = pkgs.writeText "lcdproc.conf" '' + [lcdproc] + Server=${cfg.serverHost} + Port=${toString cfg.serverPort} + ReportToSyslog=false + ${cfg.client.extraConfig} + ''; + + serviceCfg = { + DynamicUser = true; + Restart = "on-failure"; + Slice = "lcd.slice"; + }; + +in with lib; { + + meta.maintainers = with maintainers; [ peterhoeg ]; + + options = with types; { + services.hardware.lcd = { + serverHost = mkOption { + type = str; + default = "localhost"; + description = "Host on which LCDd is listening."; + }; + + serverPort = mkOption { + type = int; + default = 13666; + description = "Port on which LCDd is listening."; + }; + + server = { + enable = mkOption { + type = bool; + default = false; + description = "Enable the LCD panel server (LCDd)"; + }; + + openPorts = mkOption { + type = bool; + default = false; + description = "Open the ports in the firewall"; + }; + + usbPermissions = mkOption { + type = bool; + default = false; + description = '' + Set group-write permissions on a USB device. + </para> + <para> + A USB connected LCD panel will most likely require having its + permissions modified for lcdd to write to it. Enabling this option + sets group-write permissions on the device identified by + <option>services.hardware.lcd.usbVid</option> and + <option>services.hardware.lcd.usbPid</option>. In order to find the + values, you can run the <command>lsusb</command> command. Example + output: + </para> + <para> + <literal> + Bus 005 Device 002: ID 0403:c630 Future Technology Devices International, Ltd lcd2usb interface + </literal> + </para> + <para> + In this case the vendor id is 0403 and the product id is c630. + ''; + }; + + usbVid = mkOption { + type = str; + default = ""; + description = "The vendor ID of the USB device to claim."; + }; + + usbPid = mkOption { + type = str; + default = ""; + description = "The product ID of the USB device to claim."; + }; + + usbGroup = mkOption { + type = str; + default = "dialout"; + description = "The group to use for settings permissions. This group must exist or you will have to create it."; + }; + + extraConfig = mkOption { + type = lines; + default = ""; + description = "Additional configuration added verbatim to the server config."; + }; + }; + + client = { + enable = mkOption { + type = bool; + default = false; + description = "Enable the LCD panel client (LCDproc)"; + }; + + extraConfig = mkOption { + type = lines; + default = ""; + description = "Additional configuration added verbatim to the client config."; + }; + + restartForever = mkOption { + type = bool; + default = true; + description = "Try restarting the client forever."; + }; + }; + }; + }; + + config = mkIf (cfg.server.enable || cfg.client.enable) { + networking.firewall.allowedTCPPorts = mkIf (cfg.server.enable && cfg.server.openPorts) [ cfg.serverPort ]; + + services.udev.extraRules = mkIf (cfg.server.enable && cfg.server.usbPermissions) '' + ACTION=="add", SUBSYSTEMS=="usb", ATTRS{idVendor}=="${cfg.server.usbVid}", ATTRS{idProduct}=="${cfg.server.usbPid}", MODE="660", GROUP="${cfg.server.usbGroup}" + ''; + + systemd.services = { + lcdd = mkIf cfg.server.enable { + description = "LCDproc - server"; + wantedBy = [ "lcd.target" ]; + serviceConfig = serviceCfg // { + ExecStart = "${pkg}/bin/LCDd -f -c ${serverCfg}"; + SupplementaryGroups = cfg.server.usbGroup; + }; + }; + + lcdproc = mkIf cfg.client.enable { + description = "LCDproc - client"; + after = [ "lcdd.service" ]; + wantedBy = [ "lcd.target" ]; + serviceConfig = serviceCfg // { + ExecStart = "${pkg}/bin/lcdproc -f -c ${clientCfg}"; + # If the server is being restarted at the same time, the client will + # fail as it cannot connect, so space it out a bit. + RestartSec = "5"; + # Allow restarting for eternity + StartLimitIntervalSec = lib.mkIf cfg.client.restartForever "0"; + StartLimitBurst = lib.mkIf cfg.client.restartForever "0"; + }; + }; + }; + + systemd.targets.lcd = { + description = "LCD client/server"; + after = [ "lcdd.service" "lcdproc.service" ]; + wantedBy = [ "multi-user.target" ]; + }; + }; +} diff --git a/nixos/modules/services/hardware/trezord.nix b/nixos/modules/services/hardware/trezord.nix index 38d0a3a1d752..f2ec00a7d3e1 100644 --- a/nixos/modules/services/hardware/trezord.nix +++ b/nixos/modules/services/hardware/trezord.nix @@ -26,8 +26,15 @@ in { name = "trezord-udev-rules"; destination = "/etc/udev/rules.d/51-trezor.rules"; text = '' - SUBSYSTEM=="usb", ATTR{idVendor}=="534c", ATTR{idProduct}=="0001", MODE="0666", GROUP="dialout", SYMLINK+="trezor%n" - KERNEL=="hidraw*", ATTRS{idVendor}=="534c", ATTRS{idProduct}=="0001", MODE="0666", GROUP="dialout" + # Trezor 1 + SUBSYSTEM=="usb", ATTR{idVendor}=="534c", ATTR{idProduct}=="0001", MODE="0666", GROUP="dialout", SYMLINK+="trezor%n" + KERNEL=="hidraw*", ATTRS{idVendor}=="534c", ATTRS{idProduct}=="0001", MODE="0666", GROUP="dialout" + + # Trezor 2 (Model-T) + SUBSYSTEM=="usb", ATTR{idVendor}=="1209", ATTR{idProduct}=="53c0", MODE="0661", GROUP="dialout", TAG+="uaccess", TAG+="udev-acl", SYMLINK+="trezor%n" + SUBSYSTEM=="usb", ATTR{idVendor}=="1209", ATTR{idProduct}=="53c1", MODE="0660", GROUP="dialout", TAG+="uaccess", TAG+="udev-acl", SYMLINK+="trezor%n" + KERNEL=="hidraw*", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="53c1", MODE="0660", GROUP="dialout", TAG+="uaccess", TAG+="udev-acl" + ]; ''; }); @@ -38,7 +45,7 @@ in { path = []; serviceConfig = { Type = "simple"; - ExecStart = "${pkgs.trezord}/bin/trezord -f"; + ExecStart = "${pkgs.trezord}/bin/trezord-go"; User = "trezord"; }; }; diff --git a/nixos/modules/services/hardware/udev.nix b/nixos/modules/services/hardware/udev.nix index 9f42f9e59ad5..7bfc3bb64872 100644 --- a/nixos/modules/services/hardware/udev.nix +++ b/nixos/modules/services/hardware/udev.nix @@ -146,7 +146,7 @@ let echo "Generating hwdb database..." # hwdb --update doesn't return error code even on errors! - res="$(${udev}/bin/udevadm hwdb --update --root=$(pwd) 2>&1)" + res="$(${pkgs.buildPackages.udev}/bin/udevadm hwdb --update --root=$(pwd) 2>&1)" echo "$res" [ -z "$(echo "$res" | egrep '^Error')" ] mv etc/udev/hwdb.bin $out diff --git a/nixos/modules/services/logging/graylog.nix b/nixos/modules/services/logging/graylog.nix index a0dc0d6d089d..95f31829882f 100644 --- a/nixos/modules/services/logging/graylog.nix +++ b/nixos/modules/services/logging/graylog.nix @@ -141,7 +141,7 @@ in JAVA_HOME = jre; GRAYLOG_CONF = "${confFile}"; }; - path = [ pkgs.openjdk8 pkgs.which pkgs.procps ]; + path = [ pkgs.jre_headless pkgs.which pkgs.procps ]; preStart = '' mkdir -p /var/lib/graylog -m 755 diff --git a/nixos/modules/services/mail/dovecot.nix b/nixos/modules/services/mail/dovecot.nix index b42c73b86668..543e732127a5 100644 --- a/nixos/modules/services/mail/dovecot.nix +++ b/nixos/modules/services/mail/dovecot.nix @@ -30,6 +30,7 @@ let '' default_internal_user = ${cfg.user} + default_internal_group = ${cfg.group} ${optionalString (cfg.mailUser != null) "mail_uid = ${cfg.mailUser}"} ${optionalString (cfg.mailGroup != null) "mail_gid = ${cfg.mailGroup}"} diff --git a/nixos/modules/services/misc/defaultUnicornConfig.rb b/nixos/modules/services/misc/defaultUnicornConfig.rb index 84622622db70..0b58c59c7a51 100644 --- a/nixos/modules/services/misc/defaultUnicornConfig.rb +++ b/nixos/modules/services/misc/defaultUnicornConfig.rb @@ -1,205 +1,69 @@ -# The following was taken from github.com/crohr/syslogger and is BSD -# licensed. -require 'syslog' -require 'logger' -require 'thread' +worker_processes 3 -class Syslogger - - VERSION = "1.6.0" - - attr_reader :level, :ident, :options, :facility, :max_octets - attr_accessor :formatter - - MAPPING = { - Logger::DEBUG => Syslog::LOG_DEBUG, - Logger::INFO => Syslog::LOG_INFO, - Logger::WARN => Syslog::LOG_WARNING, - Logger::ERROR => Syslog::LOG_ERR, - Logger::FATAL => Syslog::LOG_CRIT, - Logger::UNKNOWN => Syslog::LOG_ALERT - } - - # - # Initializes default options for the logger - # <tt>ident</tt>:: the name of your program [default=$0]. - # <tt>options</tt>:: syslog options [default=<tt>Syslog::LOG_PID | Syslog::LOG_CONS</tt>]. - # Correct values are: - # LOG_CONS : writes the message on the console if an error occurs when sending the message; - # LOG_NDELAY : no delay before sending the message; - # LOG_PERROR : messages will also be written on STDERR; - # LOG_PID : adds the process number to the message (just after the program name) - # <tt>facility</tt>:: the syslog facility [default=nil] Correct values include: - # Syslog::LOG_DAEMON - # Syslog::LOG_USER - # Syslog::LOG_SYSLOG - # Syslog::LOG_LOCAL2 - # Syslog::LOG_NEWS - # etc. - # - # Usage: - # logger = Syslogger.new("my_app", Syslog::LOG_PID | Syslog::LOG_CONS, Syslog::LOG_LOCAL0) - # logger.level = Logger::INFO # use Logger levels - # logger.warn "warning message" - # logger.debug "debug message" - # - def initialize(ident = $0, options = Syslog::LOG_PID | Syslog::LOG_CONS, facility = nil) - @ident = ident - @options = options || (Syslog::LOG_PID | Syslog::LOG_CONS) - @facility = facility - @level = Logger::INFO - @mutex = Mutex.new - @formatter = Logger::Formatter.new - end - - %w{debug info warn error fatal unknown}.each do |logger_method| - # Accepting *args as message could be nil. - # Default params not supported in ruby 1.8.7 - define_method logger_method.to_sym do |*args, &block| - return true if @level > Logger.const_get(logger_method.upcase) - message = args.first || block && block.call - add(Logger.const_get(logger_method.upcase), message) - end - - unless logger_method == 'unknown' - define_method "#{logger_method}?".to_sym do - @level <= Logger.const_get(logger_method.upcase) - end - end - end - - # Log a message at the Logger::INFO level. Useful for use with Rack::CommonLogger - def write(msg) - add(Logger::INFO, msg) - end - - # Logs a message at the Logger::INFO level. - def <<(msg) - add(Logger::INFO, msg) - end - - # Low level method to add a message. - # +severity+:: the level of the message. One of Logger::DEBUG, Logger::INFO, Logger::WARN, Logger::ERROR, Logger::FATAL, Logger::UNKNOWN - # +message+:: the message string. - # If nil, the method will call the block and use the result as the message string. - # If both are nil or no block is given, it will use the progname as per the behaviour of both the standard Ruby logger, and the Rails BufferedLogger. - # +progname+:: optionally, overwrite the program name that appears in the log message. - def add(severity, message = nil, progname = nil, &block) - if message.nil? && block.nil? && !progname.nil? - message, progname = progname, nil - end - progname ||= @ident - - @mutex.synchronize do - Syslog.open(progname, @options, @facility) do |s| - s.mask = Syslog::LOG_UPTO(MAPPING[@level]) - communication = clean(message || block && block.call) - if self.max_octets - buffer = "#{tags_text}" - communication.bytes do |byte| - buffer.concat(byte) - # if the last byte we added is potentially part of an escape, we'll go ahead and add another byte - if buffer.bytesize >= self.max_octets && !['%'.ord,'\\'.ord].include?(byte) - s.log(MAPPING[severity],buffer) - buffer = "" - end - end - s.log(MAPPING[severity],buffer) unless buffer.empty? - else - s.log(MAPPING[severity],"#{tags_text}#{communication}") - end - end - end - end - - # Set the max octets of the messages written to the log - def max_octets=(max_octets) - @max_octets = max_octets - end - - # Sets the minimum level for messages to be written in the log. - # +level+:: one of <tt>Logger::DEBUG</tt>, <tt>Logger::INFO</tt>, <tt>Logger::WARN</tt>, <tt>Logger::ERROR</tt>, <tt>Logger::FATAL</tt>, <tt>Logger::UNKNOWN</tt> - def level=(level) - level = Logger.const_get(level.to_s.upcase) if level.is_a?(Symbol) - - unless level.is_a?(Fixnum) - raise ArgumentError.new("Invalid logger level `#{level.inspect}`") - end - - @level = level - end - - # Sets the ident string passed along to Syslog - def ident=(ident) - @ident = ident - end - - # Tagging code borrowed from ActiveSupport gem - def tagged(*tags) - new_tags = push_tags(*tags) - yield self - ensure - pop_tags(new_tags.size) - end - - def push_tags(*tags) - tags.flatten.reject{ |i| i.respond_to?(:empty?) ? i.empty? : !i }.tap do |new_tags| - current_tags.concat new_tags - end - end - - def pop_tags(size = 1) - current_tags.pop size - end - - def clear_tags! - current_tags.clear - end - - protected - - # Borrowed from SyslogLogger. - def clean(message) - message = message.to_s.dup - message.strip! # remove whitespace - message.gsub!(/\n/, '\\n') # escape newlines - message.gsub!(/%/, '%%') # syslog(3) freaks on % (printf) - message.gsub!(/\e\[[^m]*m/, '') # remove useless ansi color codes - message - end - - private - - def tags_text - tags = current_tags - if tags.any? - tags.collect { |tag| "[#{tag}] " }.join - end - end - - def current_tags - Thread.current[:syslogger_tagged_logging_tags] ||= [] - end -end +listen ENV["UNICORN_PATH"] + "/tmp/sockets/gitlab.socket", :backlog => 1024 +listen "/run/gitlab/gitlab.socket", :backlog => 1024 -worker_processes 2 working_directory ENV["GITLAB_PATH"] -pid ENV["UNICORN_PATH"] + "/tmp/pids/unicorn.pid" -listen ENV["UNICORN_PATH"] + "/tmp/sockets/gitlab.socket", :backlog => 1024 +pid ENV["UNICORN_PATH"] + "/tmp/pids/unicorn.pid" timeout 60 -logger Syslogger.new - +# combine Ruby 2.0.0dev or REE with "preload_app true" for memory savings +# http://rubyenterpriseedition.com/faq.html#adapt_apps_for_cow preload_app true - GC.respond_to?(:copy_on_write_friendly=) and GC.copy_on_write_friendly = true check_client_connection false +before_fork do |server, worker| + # the following is highly recommended for Rails + "preload_app true" + # as there's no need for the master process to hold a connection + defined?(ActiveRecord::Base) and + ActiveRecord::Base.connection.disconnect! + + # The following is only recommended for memory/DB-constrained + # installations. It is not needed if your system can house + # twice as many worker_processes as you have configured. + # + # This allows a new master process to incrementally + # phase out the old master process with SIGTTOU to avoid a + # thundering herd (especially in the "preload_app false" case) + # when doing a transparent upgrade. The last worker spawned + # will then kill off the old master process with a SIGQUIT. + old_pid = "#{server.config[:pid]}.oldbin" + if old_pid != server.pid + begin + sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU + Process.kill(sig, File.read(old_pid).to_i) + rescue Errno::ENOENT, Errno::ESRCH + end + end + + # Throttle the master from forking too quickly by sleeping. Due + # to the implementation of standard Unix signal handlers, this + # helps (but does not completely) prevent identical, repeated signals + # from being lost when the receiving process is busy. + # sleep 1 +end + after_fork do |server, worker| + # per-process listener ports for debugging/admin/migrations + # addr = "127.0.0.1:#{9293 + worker.nr}" + # server.listen(addr, :tries => -1, :delay => 5, :tcp_nopush => true) + + # the following is *required* for Rails + "preload_app true", defined?(ActiveRecord::Base) and ActiveRecord::Base.establish_connection + + # reset prometheus client, this will cause any opened metrics files to be closed + defined?(::Prometheus::Client.reinitialize_on_pid_change) && + Prometheus::Client.reinitialize_on_pid_change + + # if preload_app is true, then you may also want to check and + # restart any other shared sockets/descriptors such as Memcached, + # and Redis. TokyoCabinet file handles are safe to reuse + # between any number of forked children (assuming your kernel + # correctly implements pread()/pwrite() system calls) end diff --git a/nixos/modules/services/misc/disnix.nix b/nixos/modules/services/misc/disnix.nix index 39d23610b064..e4517c636e88 100644 --- a/nixos/modules/services/misc/disnix.nix +++ b/nixos/modules/services/misc/disnix.nix @@ -57,7 +57,7 @@ in ###### implementation config = mkIf cfg.enable { - dysnomia.enable = true; + services.dysnomia.enable = true; environment.systemPackages = [ pkgs.disnix ] ++ optional cfg.useWebServiceInterface pkgs.DisnixWebService; diff --git a/nixos/modules/services/misc/dysnomia.nix b/nixos/modules/services/misc/dysnomia.nix index c5c41ad296da..9e66e0811ab7 100644 --- a/nixos/modules/services/misc/dysnomia.nix +++ b/nixos/modules/services/misc/dysnomia.nix @@ -3,8 +3,8 @@ with lib; let - cfg = config.dysnomia; - + cfg = config.services.dysnomia; + printProperties = properties: concatMapStrings (propertyName: let @@ -13,7 +13,7 @@ let if isList property then "${propertyName}=(${lib.concatMapStrings (elem: "\"${toString elem}\" ") (properties."${propertyName}")})\n" else "${propertyName}=\"${toString property}\"\n" ) (builtins.attrNames properties); - + properties = pkgs.stdenv.mkDerivation { name = "dysnomia-properties"; buildCommand = '' @@ -22,13 +22,13 @@ let EOF ''; }; - + containersDir = pkgs.stdenv.mkDerivation { name = "dysnomia-containers"; buildCommand = '' mkdir -p $out cd $out - + ${concatMapStrings (containerName: let containerProperties = cfg.containers."${containerName}"; @@ -42,11 +42,11 @@ let ) (builtins.attrNames cfg.containers)} ''; }; - + linkMutableComponents = {containerName}: '' mkdir ${containerName} - + ${concatMapStrings (componentName: let component = cfg.components."${containerName}"."${componentName}"; @@ -54,13 +54,13 @@ let "ln -s ${component} ${containerName}/${componentName}\n" ) (builtins.attrNames (cfg.components."${containerName}" or {}))} ''; - + componentsDir = pkgs.stdenv.mkDerivation { name = "dysnomia-components"; buildCommand = '' mkdir -p $out cd $out - + ${concatMapStrings (containerName: let components = cfg.components."${containerName}"; @@ -72,59 +72,59 @@ let in { options = { - dysnomia = { - + services.dysnomia = { + enable = mkOption { type = types.bool; default = false; description = "Whether to enable Dysnomia"; }; - + enableAuthentication = mkOption { type = types.bool; default = false; description = "Whether to publish privacy-sensitive authentication credentials"; }; - + package = mkOption { type = types.path; description = "The Dysnomia package"; }; - + properties = mkOption { description = "An attribute set in which each attribute represents a machine property. Optionally, these values can be shell substitutions."; default = {}; }; - + containers = mkOption { description = "An attribute set in which each key represents a container and each value an attribute set providing its configuration properties"; default = {}; }; - + components = mkOption { description = "An atttribute set in which each key represents a container and each value an attribute set in which each key represents a component and each value a derivation constructing its initial state"; default = {}; }; - + extraContainerProperties = mkOption { description = "An attribute set providing additional container settings in addition to the default properties"; default = {}; }; - + extraContainerPaths = mkOption { description = "A list of paths containing additional container configurations that are added to the search folders"; default = []; }; - + extraModulePaths = mkOption { description = "A list of paths containing additional modules that are added to the search folders"; default = []; }; }; }; - + config = mkIf cfg.enable { - + environment.etc = { "dysnomia/containers" = { source = containersDir; @@ -136,16 +136,16 @@ in source = properties; }; }; - + environment.variables = { DYSNOMIA_STATEDIR = "/var/state/dysnomia-nixos"; DYSNOMIA_CONTAINERS_PATH = "${lib.concatMapStrings (containerPath: "${containerPath}:") cfg.extraContainerPaths}/etc/dysnomia/containers"; DYSNOMIA_MODULES_PATH = "${lib.concatMapStrings (modulePath: "${modulePath}:") cfg.extraModulePaths}/etc/dysnomia/modules"; }; - + environment.systemPackages = [ cfg.package ]; - - dysnomia.package = pkgs.dysnomia.override (origArgs: { + + services.dysnomia.package = pkgs.dysnomia.override (origArgs: { enableApacheWebApplication = config.services.httpd.enable; enableAxis2WebService = config.services.tomcat.axis2.enable; enableEjabberdDump = config.services.ejabberd.enable; @@ -155,10 +155,10 @@ in enableTomcatWebApplication = config.services.tomcat.enable; enableMongoDatabase = config.services.mongodb.enable; }); - - dysnomia.properties = { + + services.dysnomia.properties = { hostname = config.networking.hostName; - system = if config.nixpkgs.system == "" then builtins.currentSystem else config.nixpkgs.system; + inherit (config.nixpkgs.localSystem) system; supportedTypes = (import "${pkgs.stdenv.mkDerivation { name = "supportedtypes"; @@ -173,8 +173,8 @@ in ''; }}"); }; - - dysnomia.containers = lib.recursiveUpdate ({ + + services.dysnomia.containers = lib.recursiveUpdate ({ process = {}; wrapper = {}; } diff --git a/nixos/modules/services/misc/folding-at-home.nix b/nixos/modules/services/misc/folding-at-home.nix index 053e7e95635f..164221cbab7f 100644 --- a/nixos/modules/services/misc/folding-at-home.nix +++ b/nixos/modules/services/misc/folding-at-home.nix @@ -57,7 +57,7 @@ in { chown ${fahUser} ${stateDir} cp -f ${pkgs.writeText "client.cfg" cfg.config} ${stateDir}/client.cfg ''; - script = "${pkgs.su}/bin/su -s ${pkgs.stdenv.shell} ${fahUser} -c 'cd ${stateDir}; ${pkgs.foldingathome}/bin/fah6'"; + script = "${pkgs.su}/bin/su -s ${pkgs.runtimeShell} ${fahUser} -c 'cd ${stateDir}; ${pkgs.foldingathome}/bin/fah6'"; }; services.foldingAtHome.config = '' diff --git a/nixos/modules/services/misc/geoip-updater.nix b/nixos/modules/services/misc/geoip-updater.nix index 760fa66e80d6..e0b9df96f8e8 100644 --- a/nixos/modules/services/misc/geoip-updater.nix +++ b/nixos/modules/services/misc/geoip-updater.nix @@ -14,7 +14,7 @@ let # ExecStart= command with '@' doesn't work because we start a shell (new # process) that creates a new argv[0].) geoip-updater = pkgs.writeScriptBin "geoip-updater" '' - #!${pkgs.stdenv.shell} + #!${pkgs.runtimeShell} skipExisting=0 debug() { diff --git a/nixos/modules/services/misc/gitea.nix b/nixos/modules/services/misc/gitea.nix index f0b44b7bedeb..63e976ae566c 100644 --- a/nixos/modules/services/misc/gitea.nix +++ b/nixos/modules/services/misc/gitea.nix @@ -4,6 +4,8 @@ with lib; let cfg = config.services.gitea; + pg = config.services.postgresql; + usePostgresql = cfg.database.type == "postgres"; configFile = pkgs.writeText "app.ini" '' APP_NAME = ${cfg.appName} RUN_USER = ${cfg.user} @@ -16,6 +18,9 @@ let USER = ${cfg.database.user} PASSWD = #dbpass# PATH = ${cfg.database.path} + ${optionalString usePostgresql '' + SSL_MODE = disable + ''} [repository] ROOT = ${cfg.repositoryRoot} @@ -35,6 +40,10 @@ let SECRET_KEY = #secretkey# INSTALL_LOCK = true + [log] + ROOT_PATH = ${cfg.log.rootPath} + LEVEL = ${cfg.log.level} + ${cfg.extraConfig} ''; in @@ -60,6 +69,19 @@ in description = "gitea data directory."; }; + log = { + rootPath = mkOption { + default = "${cfg.stateDir}/log"; + type = types.str; + description = "Root path for log files."; + }; + level = mkOption { + default = "Trace"; + type = types.enum [ "Trace" "Debug" "Info" "Warn" "Error" "Critical" ]; + description = "General log level."; + }; + }; + user = mkOption { type = types.str; default = "gitea"; @@ -82,7 +104,7 @@ in port = mkOption { type = types.int; - default = 3306; + default = (if !usePostgresql then 3306 else pg.port); description = "Database host port."; }; @@ -123,6 +145,15 @@ in default = "${cfg.stateDir}/data/gitea.db"; description = "Path to the sqlite3 database file."; }; + + createDatabase = mkOption { + type = types.bool; + default = true; + description = '' + Whether to create a local postgresql database automatically. + This only applies if database type "postgres" is selected. + ''; + }; }; appName = mkOption { @@ -186,10 +217,11 @@ in }; config = mkIf cfg.enable { + services.postgresql.enable = mkIf usePostgresql (mkDefault true); systemd.services.gitea = { description = "gitea"; - after = [ "network.target" ]; + after = [ "network.target" "postgresql.service" ]; wantedBy = [ "multi-user.target" ]; path = [ pkgs.gitea.bin ]; @@ -231,12 +263,31 @@ in mkdir -p ${cfg.stateDir}/conf cp -r ${pkgs.gitea.out}/locale ${cfg.stateDir}/conf/locale fi + '' + optionalString (usePostgresql && cfg.database.createDatabase) '' + if ! test -e "${cfg.stateDir}/db-created"; then + echo "CREATE ROLE ${cfg.database.user} + WITH ENCRYPTED PASSWORD '$(head -n1 ${cfg.database.passwordFile})' + NOCREATEDB NOCREATEROLE LOGIN" | + ${pkgs.sudo}/bin/sudo -u ${pg.superUser} ${pg.package}/bin/psql + ${pkgs.sudo}/bin/sudo -u ${pg.superUser} \ + ${pg.package}/bin/createdb \ + --owner=${cfg.database.user} \ + --encoding=UTF8 \ + --lc-collate=C \ + --lc-ctype=C \ + --template=template0 \ + ${cfg.database.name} + touch "${cfg.stateDir}/db-created" + fi + '' + '' + chown ${cfg.user} -R ${cfg.stateDir} ''; serviceConfig = { Type = "simple"; User = cfg.user; WorkingDirectory = cfg.stateDir; + PermissionsStartOnly = true; ExecStart = "${pkgs.gitea.bin}/bin/gitea web"; Restart = "always"; }; @@ -253,6 +304,7 @@ in description = "Gitea Service"; home = cfg.stateDir; createHome = true; + useDefaultShell = true; }; }; diff --git a/nixos/modules/services/misc/gitit.nix b/nixos/modules/services/misc/gitit.nix index 44880ebeda14..94a98e0335df 100644 --- a/nixos/modules/services/misc/gitit.nix +++ b/nixos/modules/services/misc/gitit.nix @@ -17,7 +17,7 @@ let gititSh = hsPkgs: extras: with pkgs; let env = gititWithPkgs hsPkgs extras; in writeScript "gitit" '' - #!${stdenv.shell} + #!${runtimeShell} cd $HOME export NIX_GHC="${env}/bin/ghc" export NIX_GHCPKG="${env}/bin/ghc-pkg" diff --git a/nixos/modules/services/misc/gitlab.nix b/nixos/modules/services/misc/gitlab.nix index 9ed5875a0191..be13fed860bd 100644 --- a/nixos/modules/services/misc/gitlab.nix +++ b/nixos/modules/services/misc/gitlab.nix @@ -8,9 +8,6 @@ let cfg = config.services.gitlab; ruby = cfg.packages.gitlab.ruby; - bundler = pkgs.bundler; - - gemHome = "${cfg.packages.gitlab.rubyEnv}/${ruby.gemPath}"; gitlabSocket = "${cfg.statePath}/tmp/sockets/gitlab.socket"; gitalySocket = "${cfg.statePath}/tmp/sockets/gitaly.socket"; @@ -137,12 +134,11 @@ let gitlabEnv = { HOME = "${cfg.statePath}/home"; - GEM_HOME = gemHome; - BUNDLE_GEMFILE = "${cfg.packages.gitlab}/share/gitlab/Gemfile"; UNICORN_PATH = "${cfg.statePath}/"; GITLAB_PATH = "${cfg.packages.gitlab}/share/gitlab/"; GITLAB_STATE_PATH = "${cfg.statePath}"; GITLAB_UPLOADS_PATH = "${cfg.statePath}/uploads"; + SCHEMA = "${cfg.statePath}/db/schema.rb"; GITLAB_LOG_PATH = "${cfg.statePath}/log"; GITLAB_SHELL_PATH = "${cfg.packages.gitlab-shell}"; GITLAB_SHELL_CONFIG_PATH = "${cfg.statePath}/shell/config.yml"; @@ -157,19 +153,17 @@ let gitlab-rake = pkgs.stdenv.mkDerivation rec { name = "gitlab-rake"; - buildInputs = [ cfg.packages.gitlab cfg.packages.gitlab.rubyEnv pkgs.makeWrapper ]; - phases = "installPhase fixupPhase"; - buildPhase = ""; + buildInputs = [ pkgs.makeWrapper ]; + dontBuild = true; + unpackPhase = ":"; installPhase = '' mkdir -p $out/bin - makeWrapper ${cfg.packages.gitlab.rubyEnv}/bin/bundle $out/bin/gitlab-bundle \ + makeWrapper ${cfg.packages.gitlab.rubyEnv}/bin/rake $out/bin/gitlab-rake \ ${concatStrings (mapAttrsToList (name: value: "--set ${name} '${value}' ") gitlabEnv)} \ --set GITLAB_CONFIG_PATH '${cfg.statePath}/config' \ --set PATH '${lib.makeBinPath [ pkgs.nodejs pkgs.gzip pkgs.git pkgs.gnutar config.services.postgresql.package ]}:$PATH' \ --set RAKEOPT '-f ${cfg.packages.gitlab}/share/gitlab/Rakefile' \ --run 'cd ${cfg.packages.gitlab}/share/gitlab' - makeWrapper $out/bin/gitlab-bundle $out/bin/gitlab-rake \ - --add-flags "exec rake" ''; }; @@ -481,10 +475,10 @@ in { Type = "simple"; User = cfg.user; Group = cfg.group; - TimeoutSec = "300"; + TimeoutSec = "infinity"; Restart = "on-failure"; WorkingDirectory = "${cfg.packages.gitlab}/share/gitlab"; - ExecStart="${cfg.packages.gitlab.rubyEnv}/bin/bundle exec \"sidekiq -C \"${cfg.packages.gitlab}/share/gitlab/config/sidekiq_queues.yml\" -e production -P ${cfg.statePath}/tmp/sidekiq.pid\""; + ExecStart="${cfg.packages.gitlab.rubyEnv}/bin/sidekiq -C \"${cfg.packages.gitlab}/share/gitlab/config/sidekiq_queues.yml\" -e production -P ${cfg.statePath}/tmp/sidekiq.pid"; }; }; @@ -492,15 +486,13 @@ in { after = [ "network.target" "gitlab.service" ]; wantedBy = [ "multi-user.target" ]; environment.HOME = gitlabEnv.HOME; - environment.GEM_HOME = "${cfg.packages.gitaly.rubyEnv}/${ruby.gemPath}"; environment.GITLAB_SHELL_CONFIG_PATH = gitlabEnv.GITLAB_SHELL_CONFIG_PATH; - path = with pkgs; [ gitAndTools.git cfg.packages.gitaly.rubyEnv ruby ]; + path = with pkgs; [ gitAndTools.git cfg.packages.gitaly.rubyEnv cfg.packages.gitaly.rubyEnv.wrappedRuby ]; serviceConfig = { - #PermissionsStartOnly = true; # preStart must be run as root Type = "simple"; User = cfg.user; Group = cfg.group; - TimeoutSec = "300"; + TimeoutSec = "infinity"; Restart = "on-failure"; WorkingDirectory = gitlabEnv.HOME; ExecStart = "${cfg.packages.gitaly}/bin/gitaly ${gitalyToml}"; @@ -528,7 +520,7 @@ in { Type = "simple"; User = cfg.user; Group = cfg.group; - TimeoutSec = "300"; + TimeoutSec = "infinity"; Restart = "on-failure"; WorkingDirectory = gitlabEnv.HOME; ExecStart = @@ -566,6 +558,7 @@ in { mkdir -p ${cfg.statePath}/tmp/pids mkdir -p ${cfg.statePath}/tmp/sockets mkdir -p ${cfg.statePath}/shell + mkdir -p ${cfg.statePath}/db rm -rf ${cfg.statePath}/config ${cfg.statePath}/shell/hooks mkdir -p ${cfg.statePath}/config @@ -580,6 +573,7 @@ in { ln -sf ${cfg.statePath}/log /run/gitlab/log ln -sf ${cfg.statePath}/uploads /run/gitlab/uploads ln -sf ${cfg.statePath}/tmp /run/gitlab/tmp + ln -sf $GITLAB_SHELL_CONFIG_PATH /run/gitlab/shell-config.yml chown -R ${cfg.user}:${cfg.group} /run/gitlab # Prepare home directory @@ -587,6 +581,7 @@ in { touch ${gitlabEnv.HOME}/.ssh/authorized_keys chown -R ${cfg.user}:${cfg.group} ${gitlabEnv.HOME}/ + cp -rf ${cfg.packages.gitlab}/share/gitlab/db/* ${cfg.statePath}/db cp -rf ${cfg.packages.gitlab}/share/gitlab/config.dist/* ${cfg.statePath}/config ${optionalString cfg.smtp.enable '' ln -sf ${smtpSettings} ${cfg.statePath}/config/initializers/smtp_settings.rb @@ -654,10 +649,10 @@ in { Type = "simple"; User = cfg.user; Group = cfg.group; - TimeoutSec = "300"; + TimeoutSec = "infinity"; Restart = "on-failure"; WorkingDirectory = "${cfg.packages.gitlab}/share/gitlab"; - ExecStart = "${cfg.packages.gitlab.rubyEnv}/bin/bundle exec \"unicorn -c ${cfg.statePath}/config/unicorn.rb -E production\""; + ExecStart = "${cfg.packages.gitlab.rubyEnv}/bin/unicorn -c ${cfg.statePath}/config/unicorn.rb -E production"; }; }; diff --git a/nixos/modules/services/misc/gitweb.nix b/nixos/modules/services/misc/gitweb.nix new file mode 100644 index 000000000000..ca21366b7796 --- /dev/null +++ b/nixos/modules/services/misc/gitweb.nix @@ -0,0 +1,59 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.gitweb; + +in +{ + + options.services.gitweb = { + + projectroot = mkOption { + default = "/srv/git"; + type = types.path; + description = '' + Path to git projects (bare repositories) that should be served by + gitweb. Must not end with a slash. + ''; + }; + + extraConfig = mkOption { + default = ""; + type = types.lines; + description = '' + Verbatim configuration text appended to the generated gitweb.conf file. + ''; + example = '' + $feature{'highlight'}{'default'} = [1]; + $feature{'ctags'}{'default'} = [1]; + $feature{'avatar'}{'default'} = ['gravatar']; + ''; + }; + + gitwebTheme = mkOption { + default = false; + type = types.bool; + description = '' + Use an alternative theme for gitweb, strongly inspired by GitHub. + ''; + }; + + gitwebConfigFile = mkOption { + default = pkgs.writeText "gitweb.conf" '' + # path to git projects (<project>.git) + $projectroot = "${cfg.projectroot}"; + $highlight_bin = "${pkgs.highlight}/bin/highlight"; + ${cfg.extraConfig} + ''; + type = types.path; + readOnly = true; + internal = true; + }; + + }; + + meta.maintainers = with maintainers; [ gnidorah ]; + +} diff --git a/nixos/modules/services/misc/gogs.nix b/nixos/modules/services/misc/gogs.nix index f6d326e43d94..ba744d37e71c 100644 --- a/nixos/modules/services/misc/gogs.nix +++ b/nixos/modules/services/misc/gogs.nix @@ -35,6 +35,9 @@ let SECRET_KEY = #secretkey# INSTALL_LOCK = true + [log] + ROOT_PATH = ${cfg.stateDir}/log + ${cfg.extraConfig} ''; in diff --git a/nixos/modules/services/misc/home-assistant.nix b/nixos/modules/services/misc/home-assistant.nix index cc60a143fa6c..1dc7b44ee37b 100644 --- a/nixos/modules/services/misc/home-assistant.nix +++ b/nixos/modules/services/misc/home-assistant.nix @@ -5,7 +5,10 @@ with lib; let cfg = config.services.home-assistant; - configFile = pkgs.writeText "configuration.yaml" (builtins.toJSON cfg.config); + # cfg.config != null can be assumed here + configFile = pkgs.writeText "configuration.json" + (builtins.toJSON (if cfg.applyDefaultConfig then + (lib.recursiveUpdate defaultConfig cfg.config) else cfg.config)); availableComponents = pkgs.home-assistant.availableComponents; @@ -38,6 +41,12 @@ let then (cfg.package.override { inherit extraComponents; }) else cfg.package; + # If you are changing this, please update the description in applyDefaultConfig + defaultConfig = { + homeassistant.time_zone = config.time.timeZone; + http.server_port = (toString cfg.port); + }; + in { meta.maintainers = with maintainers; [ dotlambda ]; @@ -50,6 +59,26 @@ in { description = "The config directory, where your <filename>configuration.yaml</filename> is located."; }; + port = mkOption { + default = 8123; + type = types.int; + description = "The port on which to listen."; + }; + + applyDefaultConfig = mkOption { + default = true; + type = types.bool; + description = '' + Setting this option enables a few configuration options for HA based on NixOS configuration (such as time zone) to avoid having to manually specify configuration we already have. + </para> + <para> + Currently one side effect of enabling this is that the <literal>http</literal> component will be enabled. + </para> + <para> + This only takes effect if <literal>config != null</literal> in order to ensure that a manually managed <filename>configuration.yaml</filename> is not overwritten. + ''; + }; + config = mkOption { default = null; type = with types; nullOr attrs; @@ -104,23 +133,33 @@ in { config = mkIf cfg.enable { systemd.services.home-assistant = { description = "Home Assistant"; - wantedBy = [ "multi-user.target" ]; after = [ "network.target" ]; preStart = lib.optionalString (cfg.config != null) '' - rm -f ${cfg.configDir}/configuration.yaml - ln -s ${configFile} ${cfg.configDir}/configuration.yaml + config=${cfg.configDir}/configuration.yaml + rm -f $config + ${pkgs.remarshal}/bin/json2yaml -i ${configFile} -o $config + chmod 444 $config ''; serviceConfig = { - ExecStart = '' - ${package}/bin/hass --config "${cfg.configDir}" - ''; + ExecStart = "${package}/bin/hass --config '${cfg.configDir}'"; User = "hass"; Group = "hass"; Restart = "on-failure"; ProtectSystem = "strict"; ReadWritePaths = "${cfg.configDir}"; PrivateTmp = true; + RemoveIPC = true; }; + path = [ + "/run/wrappers" # needed for ping + ]; + }; + + systemd.targets.home-assistant = rec { + description = "Home Assistant"; + wantedBy = [ "multi-user.target" ]; + wants = [ "home-assistant.service" ]; + after = wants; }; users.extraUsers.hass = { diff --git a/nixos/modules/services/misc/ihaskell.nix b/nixos/modules/services/misc/ihaskell.nix index e07a4a44613a..6da9cc8c47e6 100644 --- a/nixos/modules/services/misc/ihaskell.nix +++ b/nixos/modules/services/misc/ihaskell.nix @@ -55,7 +55,7 @@ in serviceConfig = { User = config.users.extraUsers.ihaskell.name; Group = config.users.extraGroups.ihaskell.name; - ExecStart = "${pkgs.stdenv.shell} -c \"cd $HOME;${ihaskell}/bin/ihaskell-notebook\""; + ExecStart = "${pkgs.runtimeShell} -c \"cd $HOME;${ihaskell}/bin/ihaskell-notebook\""; }; }; }; diff --git a/nixos/modules/services/misc/logkeys.nix b/nixos/modules/services/misc/logkeys.nix index df0b3ae24c90..ad13d9eaa674 100644 --- a/nixos/modules/services/misc/logkeys.nix +++ b/nixos/modules/services/misc/logkeys.nix @@ -7,6 +7,13 @@ let in { options.services.logkeys = { enable = mkEnableOption "logkeys service"; + + device = mkOption { + description = "Use the given device as keyboard input event device instead of /dev/input/eventX default."; + default = null; + type = types.nullOr types.string; + example = "/dev/input/event15"; + }; }; config = mkIf cfg.enable { @@ -14,7 +21,7 @@ in { description = "LogKeys Keylogger Daemon"; wantedBy = [ "multi-user.target" ]; serviceConfig = { - ExecStart = "${pkgs.logkeys}/bin/logkeys -s"; + ExecStart = "${pkgs.logkeys}/bin/logkeys -s${lib.optionalString (cfg.device != null) " -d ${cfg.device}"}"; ExecStop = "${pkgs.logkeys}/bin/logkeys -k"; Type = "forking"; }; diff --git a/nixos/modules/services/misc/mesos-slave.nix b/nixos/modules/services/misc/mesos-slave.nix index 47be10274d3b..effa29b64f63 100644 --- a/nixos/modules/services/misc/mesos-slave.nix +++ b/nixos/modules/services/misc/mesos-slave.nix @@ -188,7 +188,7 @@ in { description = "Mesos Slave"; wantedBy = [ "multi-user.target" ]; after = [ "network.target" ]; - path = [ pkgs.stdenv.shellPackage ]; + path = [ pkgs.runtimeShellPackage ]; serviceConfig = { ExecStart = '' ${pkgs.mesos}/bin/mesos-slave \ @@ -213,7 +213,7 @@ in { PermissionsStartOnly = true; }; preStart = '' - mkdir -m 0700 -p ${cfg.workDir} + mkdir -m 0701 -p ${cfg.workDir} ''; }; }; diff --git a/nixos/modules/services/misc/nix-daemon.nix b/nixos/modules/services/misc/nix-daemon.nix index 72b70b28c80f..f2d34560a718 100644 --- a/nixos/modules/services/misc/nix-daemon.nix +++ b/nixos/modules/services/misc/nix-daemon.nix @@ -30,7 +30,7 @@ let # /bin/sh in the sandbox as a bind-mount to bash. This means we # also need to include the entire closure of bash. Nix >= 2.0 # provides a /bin/sh by default. - sh = pkgs.stdenv.shell; + sh = pkgs.runtimeShell; binshDeps = pkgs.writeReferencesToFile sh; in pkgs.runCommand "nix.conf" { extraOptions = cfg.extraOptions; } '' @@ -439,19 +439,18 @@ in services.xserver.displayManager.hiddenUsers = map ({ name, ... }: name) nixbldUsers; + # FIXME: use systemd-tmpfiles to create Nix directories. system.activationScripts.nix = stringAfter [ "etc" "users" ] '' # Nix initialisation. - mkdir -m 0755 -p \ + install -m 0755 -d \ /nix/var/nix/gcroots \ /nix/var/nix/temproots \ - /nix/var/nix/manifests \ /nix/var/nix/userpool \ /nix/var/nix/profiles \ /nix/var/nix/db \ - /nix/var/log/nix/drvs \ - /nix/var/nix/channel-cache - mkdir -m 1777 -p \ + /nix/var/log/nix/drvs + install -m 1777 -d \ /nix/var/nix/gcroots/per-user \ /nix/var/nix/profiles/per-user \ /nix/var/nix/gcroots/tmp diff --git a/nixos/modules/services/misc/nixos-manual.nix b/nixos/modules/services/misc/nixos-manual.nix index 5d0f2abd13a9..4bd1c20edf71 100644 --- a/nixos/modules/services/misc/nixos-manual.nix +++ b/nixos/modules/services/misc/nixos-manual.nix @@ -23,7 +23,7 @@ let options = let scrubbedEval = evalModules { - modules = [ { nixpkgs.system = config.nixpkgs.system; } ] ++ baseModules; + modules = [ { nixpkgs.localSystem = config.nixpkgs.localSystem; } ] ++ baseModules; args = (config._module.args) // { modules = [ ]; }; specialArgs = { pkgs = scrubDerivations "pkgs" pkgs; }; }; @@ -43,7 +43,7 @@ let helpScript = pkgs.writeScriptBin "nixos-help" '' - #! ${pkgs.stdenv.shell} -e + #! ${pkgs.runtimeShell} -e browser="$BROWSER" if [ -z "$browser" ]; then browser="$(type -P xdg-open || true)" @@ -112,10 +112,10 @@ in system.build.manual = manual; - environment.systemPackages = - [ manual.manual helpScript ] - ++ optionals config.services.xserver.enable [desktopItem pkgs.nixos-icons] - ++ optional config.programs.man.enable manual.manpages; + environment.systemPackages = [] + ++ optionals config.services.xserver.enable [ desktopItem pkgs.nixos-icons ] + ++ optional config.documentation.man.enable manual.manpages + ++ optionals config.documentation.doc.enable [ manual.manual helpScript ]; boot.extraTTYs = mkIf cfg.showManual ["tty${toString cfg.ttyNumber}"]; diff --git a/nixos/modules/services/misc/parsoid.nix b/nixos/modules/services/misc/parsoid.nix index ae3f84333d2d..c757093e5c1b 100644 --- a/nixos/modules/services/misc/parsoid.nix +++ b/nixos/modules/services/misc/parsoid.nix @@ -6,6 +6,8 @@ let cfg = config.services.parsoid; + parsoid = pkgs.nodePackages."parsoid-git://github.com/abbradar/parsoid#stable"; + confTree = { worker_heartbeat_timeout = 300000; logging = { level = "info"; }; @@ -93,7 +95,7 @@ in after = [ "network.target" ]; serviceConfig = { User = "nobody"; - ExecStart = "${pkgs.nodePackages.parsoid}/lib/node_modules/parsoid/bin/server.js -c ${confFile} -n ${toString cfg.workers}"; + ExecStart = "${parsoid}/lib/node_modules/parsoid/bin/server.js -c ${confFile} -n ${toString cfg.workers}"; }; }; diff --git a/nixos/modules/services/misc/safeeyes.nix b/nixos/modules/services/misc/safeeyes.nix new file mode 100644 index 000000000000..1a33971d9227 --- /dev/null +++ b/nixos/modules/services/misc/safeeyes.nix @@ -0,0 +1,50 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.safeeyes; + +in + +{ + + ###### interface + + options = { + + services.safeeyes = { + + enable = mkOption { + default = false; + description = "Whether to enable the safeeyes OSGi service"; + }; + + }; + + }; + + ###### implementation + + config = mkIf cfg.enable { + + systemd.user.services.safeeyes = { + description = "Safeeyes"; + + wantedBy = [ "graphical-session.target" ]; + partOf = [ "graphical-session.target" ]; + + serviceConfig = { + ExecStart = '' + ${pkgs.safeeyes}/bin/safeeyes + ''; + Restart = "on-failure"; + RestartSec = 3; + StartLimitInterval = 350; + StartLimitBurst = 10; + }; + }; + + }; +} diff --git a/nixos/modules/services/misc/serviio.nix b/nixos/modules/services/misc/serviio.nix new file mode 100644 index 000000000000..a6612e9c6adb --- /dev/null +++ b/nixos/modules/services/misc/serviio.nix @@ -0,0 +1,92 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.serviio; + + serviioStart = pkgs.writeScript "serviio.sh" '' + #!${pkgs.bash}/bin/sh + + SERVIIO_HOME=${pkgs.serviio} + + # Setup the classpath + SERVIIO_CLASS_PATH="$SERVIIO_HOME/lib/*:$SERVIIO_HOME/config" + + # Setup Serviio specific properties + JAVA_OPTS="-Djava.net.preferIPv4Stack=true -Djava.awt.headless=true -Dorg.restlet.engine.loggerFacadeClass=org.restlet.ext.slf4j.Slf4jLoggerFacade + -Dderby.system.home=${cfg.dataDir}/library -Dserviio.home=${cfg.dataDir} -Dffmpeg.location=${pkgs.ffmpeg}/bin/ffmpeg -Ddcraw.location=${pkgs.dcraw}/bin/dcraw" + + # Execute the JVM in the foreground + exec ${pkgs.jre}/bin/java -Xmx512M -Xms20M -XX:+UseG1GC -XX:GCTimeRatio=1 -XX:MinHeapFreeRatio=10 -XX:MaxHeapFreeRatio=20 $JAVA_OPTS -classpath "$SERVIIO_CLASS_PATH" org.serviio.MediaServer "$@" + ''; + +in { + + ###### interface + options = { + services.serviio = { + + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable the Serviio Media Server. + ''; + }; + + dataDir = mkOption { + type = types.path; + default = "/var/lib/serviio"; + description = '' + The directory where serviio stores its state, data, etc. + ''; + }; + + }; + }; + + ###### implementation + + config = mkIf cfg.enable { + systemd.services.serviio = { + description = "Serviio Media Server"; + after = [ "local-fs.target" "network.target" ]; + wantedBy = [ "multi-user.target" ]; + path = [ pkgs.serviio ]; + serviceConfig = { + User = "serviio"; + Group = "serviio"; + ExecStart = "${serviioStart}"; + ExecStop = "${serviioStart} -stop"; + }; + }; + + users.extraUsers = [ + { + name = "serviio"; + group = "serviio"; + home = cfg.dataDir; + description = "Serviio Media Server User"; + createHome = true; + isSystemUser = true; + } + ]; + + users.extraGroups = [ + { name = "serviio";} + ]; + + networking.firewall = { + allowedTCPPorts = [ + 8895 # serve UPnP responses + 23423 # console + 23424 # mediabrowser + ]; + allowedUDPPorts = [ + 1900 # UPnP service discovey + ]; + }; + }; +} diff --git a/nixos/modules/services/misc/ssm-agent.nix b/nixos/modules/services/misc/ssm-agent.nix index a57fbca86fb6..e951a4c7ffa8 100644 --- a/nixos/modules/services/misc/ssm-agent.nix +++ b/nixos/modules/services/misc/ssm-agent.nix @@ -8,7 +8,7 @@ let # in nixpkgs doesn't seem to work properly on NixOS, so let's just fake the two fields SSM # looks for. See https://github.com/aws/amazon-ssm-agent/issues/38 for upstream fix. fake-lsb-release = pkgs.writeScriptBin "lsb_release" '' - #!${pkgs.stdenv.shell} + #!${pkgs.runtimeShell} case "$1" in -i) echo "nixos";; diff --git a/nixos/modules/services/monitoring/apcupsd.nix b/nixos/modules/services/monitoring/apcupsd.nix index 9abd6e9ab641..839116de6265 100644 --- a/nixos/modules/services/monitoring/apcupsd.nix +++ b/nixos/modules/services/monitoring/apcupsd.nix @@ -38,7 +38,7 @@ let ]; shellCmdsForEventScript = eventname: commands: '' - echo "#!${pkgs.stdenv.shell}" > "$out/${eventname}" + echo "#!${pkgs.runtimeShell}" > "$out/${eventname}" echo '${commands}' >> "$out/${eventname}" chmod a+x "$out/${eventname}" ''; diff --git a/nixos/modules/services/monitoring/grafana.nix b/nixos/modules/services/monitoring/grafana.nix index 921be23f3681..eceb91525db4 100644 --- a/nixos/modules/services/monitoring/grafana.nix +++ b/nixos/modules/services/monitoring/grafana.nix @@ -25,6 +25,7 @@ let DATABASE_USER = cfg.database.user; DATABASE_PASSWORD = cfg.database.password; DATABASE_PATH = cfg.database.path; + DATABASE_CONN_MAX_LIFETIME = cfg.database.connMaxLifetime; SECURITY_ADMIN_USER = cfg.security.adminUser; SECURITY_ADMIN_PASSWORD = cfg.security.adminPassword; @@ -49,7 +50,7 @@ in { protocol = mkOption { description = "Which protocol to listen."; default = "http"; - type = types.enum ["http" "https"]; + type = types.enum ["http" "https" "socket"]; }; addr = mkOption { @@ -143,6 +144,15 @@ in { default = "${cfg.dataDir}/data/grafana.db"; type = types.path; }; + + connMaxLifetime = mkOption { + description = '' + Sets the maximum amount of time (in seconds) a connection may be reused. + For MySQL this setting should be shorter than the `wait_timeout' variable. + ''; + default = 14400; + type = types.int; + }; }; security = { @@ -241,7 +251,9 @@ in { description = "Grafana Service Daemon"; wantedBy = ["multi-user.target"]; after = ["networking.target"]; - environment = mapAttrs' (n: v: nameValuePair "GF_${n}" (toString v)) envOptions; + environment = { + QT_QPA_PLATFORM = "offscreen"; + } // mapAttrs' (n: v: nameValuePair "GF_${n}" (toString v)) envOptions; serviceConfig = { ExecStart = "${cfg.package.bin}/bin/grafana-server -homepath ${cfg.dataDir}"; WorkingDirectory = cfg.dataDir; diff --git a/nixos/modules/services/monitoring/monit.nix b/nixos/modules/services/monitoring/monit.nix index 71f50cc0f19d..d48e5c550abb 100644 --- a/nixos/modules/services/monitoring/monit.nix +++ b/nixos/modules/services/monitoring/monit.nix @@ -26,16 +26,10 @@ in environment.systemPackages = [ pkgs.monit ]; - environment.etc = [ - { - source = pkgs.writeTextFile { - name = "monitrc"; - text = config.services.monit.config; - }; - target = "monitrc"; - mode = "0400"; - } - ]; + environment.etc."monitrc" = { + text = config.services.monit.config; + mode = "0400"; + }; systemd.services.monit = { description = "Pro-active monitoring utility for unix systems"; @@ -48,6 +42,8 @@ in KillMode = "process"; Restart = "always"; }; + restartTriggers = [ config.environment.etc."monitrc".source ]; }; + }; } diff --git a/nixos/modules/services/monitoring/prometheus/blackbox-exporter.nix b/nixos/modules/services/monitoring/prometheus/blackbox-exporter.nix deleted file mode 100644 index ce2e1cf2d74b..000000000000 --- a/nixos/modules/services/monitoring/prometheus/blackbox-exporter.nix +++ /dev/null @@ -1,68 +0,0 @@ -{ config, pkgs, lib, ... }: - -with lib; - -let - cfg = config.services.prometheus.blackboxExporter; -in { - options = { - services.prometheus.blackboxExporter = { - enable = mkEnableOption "prometheus blackbox exporter"; - - configFile = mkOption { - type = types.path; - description = '' - Path to configuration file. - ''; - }; - - port = mkOption { - type = types.int; - default = 9115; - description = '' - Port to listen on. - ''; - }; - - extraFlags = mkOption { - type = types.listOf types.str; - default = []; - description = '' - Extra commandline options when launching the blackbox exporter. - ''; - }; - - openFirewall = mkOption { - type = types.bool; - default = false; - description = '' - Open port in firewall for incoming connections. - ''; - }; - }; - }; - - config = mkIf cfg.enable { - networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port; - - systemd.services.prometheus-blackbox-exporter = { - description = "Prometheus exporter for blackbox probes"; - unitConfig.Documentation = "https://github.com/prometheus/blackbox_exporter"; - wantedBy = [ "multi-user.target" ]; - serviceConfig = { - User = "nobody"; - Restart = "always"; - PrivateTmp = true; - WorkingDirectory = /tmp; - AmbientCapabilities = [ "CAP_NET_RAW" ]; # for ping probes - ExecStart = '' - ${pkgs.prometheus-blackbox-exporter}/bin/blackbox_exporter \ - --web.listen-address :${toString cfg.port} \ - --config.file ${cfg.configFile} \ - ${concatStringsSep " \\\n " cfg.extraFlags} - ''; - ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; - }; - }; - }; -} diff --git a/nixos/modules/services/monitoring/prometheus/collectd-exporter.nix b/nixos/modules/services/monitoring/prometheus/collectd-exporter.nix deleted file mode 100644 index f8a5b9576a11..000000000000 --- a/nixos/modules/services/monitoring/prometheus/collectd-exporter.nix +++ /dev/null @@ -1,128 +0,0 @@ -{ config, pkgs, lib, ... }: - -with lib; - -let - cfg = config.services.prometheus.collectdExporter; - - collectSettingsArgs = if (cfg.collectdBinary.enable) then '' - -collectd.listen-address ${optionalString (cfg.collectdBinary.listenAddress != null) cfg.collectdBinary.listenAddress}:${toString cfg.collectdBinary.port} \ - -collectd.security-level ${cfg.collectdBinary.securityLevel} \ - '' else ""; - -in { - options = { - services.prometheus.collectdExporter = { - enable = mkEnableOption "prometheus collectd exporter"; - - port = mkOption { - type = types.int; - default = 9103; - description = '' - Port to listen on. - This is used for scraping as well as the to receive collectd data via the write_http plugin. - ''; - }; - - listenAddress = mkOption { - type = types.nullOr types.str; - default = null; - example = "0.0.0.0"; - description = '' - Address to listen on for web interface, telemetry and collectd JSON data. - ''; - }; - - collectdBinary = { - enable = mkEnableOption "collectd binary protocol receiver"; - - authFile = mkOption { - default = null; - type = types.nullOr types.path; - description = "File mapping user names to pre-shared keys (passwords)."; - }; - - port = mkOption { - type = types.int; - default = 25826; - description = ''Network address on which to accept collectd binary network packets.''; - }; - - listenAddress = mkOption { - type = types.nullOr types.str; - default = null; - example = "0.0.0.0"; - description = '' - Address to listen on for binary network packets. - ''; - }; - - securityLevel = mkOption { - type = types.enum ["None" "Sign" "Encrypt"]; - default = "None"; - description = '' - Minimum required security level for accepted packets. - ''; - }; - }; - - extraFlags = mkOption { - type = types.listOf types.str; - default = []; - description = '' - Extra commandline options when launching the collectd exporter. - ''; - }; - - logFormat = mkOption { - type = types.str; - default = "logger:stderr"; - example = "logger:syslog?appname=bob&local=7 or logger:stdout?json=true"; - description = '' - Set the log target and format. - ''; - }; - - logLevel = mkOption { - type = types.enum ["debug" "info" "warn" "error" "fatal"]; - default = "info"; - description = '' - Only log messages with the given severity or above. - ''; - }; - - openFirewall = mkOption { - type = types.bool; - default = false; - description = '' - Open port in firewall for incoming connections. - ''; - }; - }; - }; - - config = mkIf cfg.enable { - networking.firewall.allowedTCPPorts = (optional cfg.openFirewall cfg.port) ++ - (optional (cfg.openFirewall && cfg.collectdBinary.enable) cfg.collectdBinary.port); - - systemd.services.prometheus-collectd-exporter = { - description = "Prometheus exporter for Collectd metrics"; - unitConfig.Documentation = "https://github.com/prometheus/collectd_exporter"; - wantedBy = [ "multi-user.target" ]; - serviceConfig = { - DynamicUser = true; - Restart = "always"; - PrivateTmp = true; - WorkingDirectory = /tmp; - ExecStart = '' - ${pkgs.prometheus-collectd-exporter}/bin/collectd_exporter \ - -log.format ${cfg.logFormat} \ - -log.level ${cfg.logLevel} \ - -web.listen-address ${optionalString (cfg.listenAddress != null) cfg.listenAddress}:${toString cfg.port} \ - ${collectSettingsArgs} \ - ${concatStringsSep " " cfg.extraFlags} - ''; - }; - }; - }; -} diff --git a/nixos/modules/services/monitoring/prometheus/exporters.nix b/nixos/modules/services/monitoring/prometheus/exporters.nix new file mode 100644 index 000000000000..180b273d177e --- /dev/null +++ b/nixos/modules/services/monitoring/prometheus/exporters.nix @@ -0,0 +1,173 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let + cfg = config.services.prometheus.exporters; + + # each attribute in `exporterOpts` is expected to have specified: + # - port (types.int): port on which the exporter listens + # - serviceOpts (types.attrs): config that is merged with the + # default definition of the exporter's + # systemd service + # - extraOpts (types.attrs): extra configuration options to + # configure the exporter with, which + # are appended to the default options + # + # Note that `extraOpts` is optional, but a script for the exporter's + # systemd service must be provided by specifying either + # `serviceOpts.script` or `serviceOpts.serviceConfig.ExecStart` + exporterOpts = { + blackbox = import ./exporters/blackbox.nix { inherit config lib pkgs; }; + collectd = import ./exporters/collectd.nix { inherit config lib pkgs; }; + dovecot = import ./exporters/dovecot.nix { inherit config lib pkgs; }; + fritzbox = import ./exporters/fritzbox.nix { inherit config lib pkgs; }; + json = import ./exporters/json.nix { inherit config lib pkgs; }; + minio = import ./exporters/minio.nix { inherit config lib pkgs; }; + nginx = import ./exporters/nginx.nix { inherit config lib pkgs; }; + node = import ./exporters/node.nix { inherit config lib pkgs; }; + postfix = import ./exporters/postfix.nix { inherit config lib pkgs; }; + snmp = import ./exporters/snmp.nix { inherit config lib pkgs; }; + unifi = import ./exporters/unifi.nix { inherit config lib pkgs; }; + varnish = import ./exporters/varnish.nix { inherit config lib pkgs; }; + }; + + mkExporterOpts = ({ name, port }: { + enable = mkEnableOption "the prometheus ${name} exporter"; + port = mkOption { + type = types.int; + default = port; + description = '' + Port to listen on. + ''; + }; + listenAddress = mkOption { + type = types.str; + default = "0.0.0.0"; + description = '' + Address to listen on. + ''; + }; + extraFlags = mkOption { + type = types.listOf types.str; + default = []; + description = '' + Extra commandline options to pass to the ${name} exporter. + ''; + }; + openFirewall = mkOption { + type = types.bool; + default = false; + description = '' + Open port in firewall for incoming connections. + ''; + }; + firewallFilter = mkOption { + type = types.str; + default = "-p tcp -m tcp --dport ${toString port}"; + example = literalExample '' + "-i eth0 -p tcp -m tcp --dport ${toString port}" + ''; + description = '' + Specify a filter for iptables to use when + <option>services.prometheus.exporters.${name}.openFirewall</option> + is true. It is used as `ip46tables -I INPUT <option>firewallFilter</option> -j ACCEPT`. + ''; + }; + user = mkOption { + type = types.str; + default = "nobody"; + description = '' + User name under which the ${name} exporter shall be run. + Has no effect when <option>systemd.services.prometheus-${name}-exporter.serviceConfig.DynamicUser</option> is true. + ''; + }; + group = mkOption { + type = types.str; + default = "nobody"; + description = '' + Group under which the ${name} exporter shall be run. + Has no effect when <option>systemd.services.prometheus-${name}-exporter.serviceConfig.DynamicUser</option> is true. + ''; + }; + }); + + mkSubModule = { name, port, extraOpts, serviceOpts }: { + ${name} = mkOption { + type = types.submodule { + options = (mkExporterOpts { + inherit name port; + } // extraOpts); + }; + internal = true; + default = {}; + }; + }; + + mkSubModules = (foldl' (a: b: a//b) {} + (mapAttrsToList (name: opts: mkSubModule { + inherit name; + inherit (opts) port serviceOpts; + extraOpts = opts.extraOpts or {}; + }) exporterOpts) + ); + + mkExporterConf = { name, conf, serviceOpts }: + mkIf conf.enable { + networking.firewall.extraCommands = mkIf conf.openFirewall '' + ip46tables -I INPUT ${conf.firewallFilter} -j ACCEPT + ''; + 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 ]); + }; +in +{ + options.services.prometheus.exporters = mkOption { + type = types.submodule { + options = (mkSubModules); + }; + description = "Prometheus exporter configuration"; + default = {}; + example = literalExample '' + { + node = { + enable = true; + enabledCollectors = [ "systemd" ]; + }; + varnish.enable = true; + } + ''; + }; + + config = mkMerge ([{ + assertions = [{ + assertion = (cfg.snmp.configurationPath == null) != (cfg.snmp.configuration == null); + message = '' + Please ensure you have either `services.prometheus.exporters.snmp.configuration' + or `services.prometheus.exporters.snmp.configurationPath' set! + ''; + }]; + }] ++ [(mkIf config.services.minio.enable { + services.prometheus.exporters.minio.minioAddress = mkDefault "http://localhost:9000"; + services.prometheus.exporters.minio.minioAccessKey = mkDefault config.services.minio.accessKey; + services.prometheus.exporters.minio.minioAccessSecret = mkDefault config.services.minio.secretKey; + })] ++ (mapAttrsToList (name: conf: + mkExporterConf { + inherit name; + inherit (conf) serviceOpts; + conf = cfg.${name}; + }) exporterOpts) + ); + + meta.doc = ./exporters.xml; +} diff --git a/nixos/modules/services/monitoring/prometheus/exporters.xml b/nixos/modules/services/monitoring/prometheus/exporters.xml new file mode 100644 index 000000000000..4f0bcb298106 --- /dev/null +++ b/nixos/modules/services/monitoring/prometheus/exporters.xml @@ -0,0 +1,135 @@ +<chapter xmlns="http://docbook.org/ns/docbook" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:xi="http://www.w3.org/2001/XInclude" + version="5.0" + xml:id="module-services-prometheus-exporters"> + +<title>Prometheus exporters</title> + +<para>Prometheus exporters provide metrics for the <link xlink:href="https://prometheus.io">prometheus monitoring system</link>.</para> + +<section><title>Configuration</title> + <para>One of the most common exporters is the <link xlink:href="https://github.com/prometheus/node_exporter">node exporter</link>, it provides hardware and OS metrics from the host it's running on. The exporter could be configured as follows: +<programlisting> + services.promtheus.exporters.node = { + enable = true; + enabledCollectors = [ + "logind" + "systemd" + ]; + disabledCollectors = [ + "textfile" + ]; + openFirewall = true; + firewallFilter = "-i br0 -p tcp -m tcp --dport 9100"; + }; +</programlisting> +It should now serve all metrics from the collectors +that are explicitly enabled and the ones that are +<link xlink:href="https://github.com/prometheus/node_exporter#enabled-by-default">enabled by default</link>, via http under <literal>/metrics</literal>. In this example the firewall should just +allow incoming connections to the exporter's port on the bridge interface <literal>br0</literal> +(this would have to be configured seperately of course). +For more information about configuration see <literal>man configuration.nix</literal> or +search through the <link xlink:href="https://nixos.org/nixos/options.html#prometheus.exporters">available options</link>. +</para> +</section> +<section><title>Adding a new exporter</title> + <para>To add a new exporter, it has to be packaged first (see <literal>nixpkgs/pkgs/servers/monitoring/prometheus/</literal> for examples), then a module can be added. The postfix exporter is used in this example:</para> +<itemizedlist> + <listitem> + <para> + Some default options for all exporters are provided by + <literal>nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix</literal>: + </para> + </listitem> + <listitem override='none'> + <itemizedlist> + <listitem><para><literal>enable</literal></para></listitem> + <listitem><para><literal>port</literal></para></listitem> + <listitem><para><literal>listenAddress</literal></para></listitem> + <listitem><para><literal>extraFlags</literal></para></listitem> + <listitem><para><literal>openFirewall</literal></para></listitem> + <listitem><para><literal>firewallFilter</literal></para></listitem> + <listitem><para><literal>user</literal></para></listitem> + <listitem><para><literal>group</literal></para></listitem> + </itemizedlist> + </listitem> + <listitem> + <para>As there is already a package available, the module can now be added. + This is accomplished by adding a new file to the + <literal>nixos/modules/services/monitoring/prometheus/exporters/</literal> directory, + which will be called postfix.nix and contains all exporter specific options + and configuration: + <programlisting> + # nixpgs/nixos/modules/services/prometheus/exporters/postfix.nix + { config, lib, pkgs }: + + with lib; + + let + # for convenience we define cfg here + cfg = config.services.prometheus.exporters.postfix; + in + { + port = 9154; # The postfix exporter listens on this port by default + + # `extraOpts` is an attribute set which contains additional options + # (and optional overrides for default options). + # Note that this attribute is optional. + extraOpts = { + telemetryPath = mkOption { + type = types.str; + default = "/metrics"; + description = '' + Path under which to expose metrics. + ''; + }; + logfilePath = mkOption { + type = types.path; + default = /var/log/postfix_exporter_input.log; + example = /var/log/mail.log; + description = '' + Path where Postfix writes log entries. + This file will be truncated by this exporter! + ''; + }; + showqPath = mkOption { + type = types.path; + default = /var/spool/postfix/public/showq; + example = /var/lib/postfix/queue/public/showq; + description = '' + Path at which Postfix places its showq socket. + ''; + }; + }; + + # `serviceOpts` is an attribute set which contains configuration + # for the exporter's systemd service. One of + # `serviceOpts.script` and `serviceOpts.serviceConfig.ExecStart` + # has to be specified here. This will be merged with the default + # service confiuration. + serviceOpts = { + serviceConfig = { + ExecStart = '' + ${pkgs.prometheus-postfix-exporter}/bin/postfix_exporter \ + --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \ + --web.telemetry-path ${cfg.telemetryPath} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + }; + }; + } + </programlisting> + </para> + </listitem> + <listitem> + <para> + This should already be enough for the postfix exporter. Additionally one could + now add assertions and conditional default values. This can be done in the + 'meta-module' that combines all exporter definitions and generates the submodules: + <literal>nixpkgs/nixos/modules/services/prometheus/exporters.nix</literal> + </para> + </listitem> +</itemizedlist> +</section> +</chapter> diff --git a/nixos/modules/services/monitoring/prometheus/exporters/blackbox.nix b/nixos/modules/services/monitoring/prometheus/exporters/blackbox.nix new file mode 100644 index 000000000000..d09d1c4f3663 --- /dev/null +++ b/nixos/modules/services/monitoring/prometheus/exporters/blackbox.nix @@ -0,0 +1,31 @@ +{ config, lib, pkgs }: + +with lib; + +let + cfg = config.services.prometheus.exporters.blackbox; +in +{ + port = 9115; + extraOpts = { + configFile = mkOption { + type = types.path; + description = '' + Path to configuration file. + ''; + }; + }; + serviceOpts = { + serviceConfig = { + AmbientCapabilities = [ "CAP_NET_RAW" ]; # for ping probes + DynamicUser = true; + ExecStart = '' + ${pkgs.prometheus-blackbox-exporter}/bin/blackbox_exporter \ + --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \ + --config.file ${cfg.configFile} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + }; + }; +} diff --git a/nixos/modules/services/monitoring/prometheus/exporters/collectd.nix b/nixos/modules/services/monitoring/prometheus/exporters/collectd.nix new file mode 100644 index 000000000000..0eba3527162d --- /dev/null +++ b/nixos/modules/services/monitoring/prometheus/exporters/collectd.nix @@ -0,0 +1,78 @@ +{ config, lib, pkgs }: + +with lib; + +let + cfg = config.services.prometheus.exporters.collectd; +in +{ + port = 9103; + extraOpts = { + collectdBinary = { + enable = mkEnableOption "collectd binary protocol receiver"; + + authFile = mkOption { + default = null; + type = types.nullOr types.path; + description = "File mapping user names to pre-shared keys (passwords)."; + }; + + port = mkOption { + type = types.int; + default = 25826; + description = ''Network address on which to accept collectd binary network packets.''; + }; + + listenAddress = mkOption { + type = types.str; + default = "0.0.0.0"; + description = '' + Address to listen on for binary network packets. + ''; + }; + + securityLevel = mkOption { + type = types.enum ["None" "Sign" "Encrypt"]; + default = "None"; + description = '' + Minimum required security level for accepted packets. + ''; + }; + }; + + logFormat = mkOption { + type = types.str; + default = "logger:stderr"; + example = "logger:syslog?appname=bob&local=7 or logger:stdout?json=true"; + description = '' + Set the log target and format. + ''; + }; + + logLevel = mkOption { + type = types.enum ["debug" "info" "warn" "error" "fatal"]; + default = "info"; + description = '' + Only log messages with the given severity or above. + ''; + }; + }; + serviceOpts = let + collectSettingsArgs = if (cfg.collectdBinary.enable) then '' + -collectd.listen-address ${cfg.collectdBinary.listenAddress}:${toString cfg.collectdBinary.port} \ + -collectd.security-level ${cfg.collectdBinary.securityLevel} \ + '' else ""; + in { + serviceConfig = { + DynamicUser = true; + ExecStart = '' + ${pkgs.prometheus-collectd-exporter}/bin/collectd_exporter \ + -log.format ${cfg.logFormat} \ + -log.level ${cfg.logLevel} \ + -web.listen-address ${cfg.listenAddress}:${toString cfg.port} \ + ${collectSettingsArgs} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + }; + }; +} diff --git a/nixos/modules/services/monitoring/prometheus/exporters/dovecot.nix b/nixos/modules/services/monitoring/prometheus/exporters/dovecot.nix new file mode 100644 index 000000000000..4ca6d4e5f8b6 --- /dev/null +++ b/nixos/modules/services/monitoring/prometheus/exporters/dovecot.nix @@ -0,0 +1,50 @@ +{ config, lib, pkgs }: + +with lib; + +let + cfg = config.services.prometheus.exporters.dovecot; +in +{ + port = 9166; + extraOpts = { + telemetryPath = mkOption { + type = types.str; + default = "/metrics"; + description = '' + Path under which to expose metrics. + ''; + }; + socketPath = mkOption { + type = types.path; + default = "/var/run/dovecot/stats"; + example = "/var/run/dovecot2/stats"; + description = '' + Path under which the stats socket is placed. + The user/group under which the exporter runs, + should be able to access the socket in order + to scrape the metrics successfully. + ''; + }; + scopes = mkOption { + type = types.listOf types.str; + default = [ "user" ]; + example = [ "user" "global" ]; + description = '' + Stats scopes to query. + ''; + }; + }; + serviceOpts = { + serviceConfig = { + ExecStart = '' + ${pkgs.prometheus-dovecot-exporter}/bin/dovecot_exporter \ + --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \ + --web.telemetry-path ${cfg.telemetryPath} \ + --dovecot.socket-path ${cfg.socketPath} \ + --dovecot.scopes ${concatStringsSep "," cfg.scopes} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + }; + }; +} diff --git a/nixos/modules/services/monitoring/prometheus/exporters/fritzbox.nix b/nixos/modules/services/monitoring/prometheus/exporters/fritzbox.nix new file mode 100644 index 000000000000..a3f1d9d31323 --- /dev/null +++ b/nixos/modules/services/monitoring/prometheus/exporters/fritzbox.nix @@ -0,0 +1,39 @@ +{ config, lib, pkgs }: + +with lib; + +let + cfg = config.services.prometheus.exporters.fritzbox; +in +{ + port = 9133; + extraOpts = { + gatewayAddress = mkOption { + type = types.str; + default = "fritz.box"; + description = '' + The hostname or IP of the FRITZ!Box. + ''; + }; + + gatewayPort = mkOption { + type = types.int; + default = 49000; + description = '' + The port of the FRITZ!Box UPnP service. + ''; + }; + }; + serviceOpts = { + serviceConfig = { + DynamicUser = true; + ExecStart = '' + ${pkgs.prometheus-fritzbox-exporter}/bin/fritzbox_exporter \ + -listen-address ${cfg.listenAddress}:${toString cfg.port} \ + -gateway-address ${cfg.gatewayAddress} \ + -gateway-port ${toString cfg.gatewayPort} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + }; + }; +} diff --git a/nixos/modules/services/monitoring/prometheus/exporters/json.nix b/nixos/modules/services/monitoring/prometheus/exporters/json.nix new file mode 100644 index 000000000000..a5494e85e016 --- /dev/null +++ b/nixos/modules/services/monitoring/prometheus/exporters/json.nix @@ -0,0 +1,36 @@ +{ config, lib, pkgs }: + +with lib; + +let + cfg = config.services.prometheus.exporters.json; +in +{ + port = 7979; + extraOpts = { + url = mkOption { + type = types.str; + description = '' + URL to scrape JSON from. + ''; + }; + configFile = mkOption { + type = types.path; + description = '' + Path to configuration file. + ''; + }; + listenAddress = {}; # not used + }; + serviceOpts = { + serviceConfig = { + DynamicUser = true; + ExecStart = '' + ${pkgs.prometheus-json-exporter}/bin/prometheus-json-exporter \ + --port ${toString cfg.port} \ + ${cfg.url} ${cfg.configFile} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + }; + }; +} diff --git a/nixos/modules/services/monitoring/prometheus/exporters/minio.nix b/nixos/modules/services/monitoring/prometheus/exporters/minio.nix new file mode 100644 index 000000000000..3cc4ffdbc8fd --- /dev/null +++ b/nixos/modules/services/monitoring/prometheus/exporters/minio.nix @@ -0,0 +1,65 @@ +{ config, lib, pkgs }: + +with lib; + +let + cfg = config.services.prometheus.exporters.minio; +in +{ + port = 9290; + extraOpts = { + minioAddress = mkOption { + type = types.str; + example = "https://10.0.0.1:9000"; + description = '' + The URL of the minio server. + Use HTTPS if Minio accepts secure connections only. + By default this connects to the local minio server if enabled. + ''; + }; + + minioAccessKey = mkOption { + type = types.str; + example = "yourMinioAccessKey"; + description = '' + The value of the Minio access key. + It is required in order to connect to the server. + By default this uses the one from the local minio server if enabled + and <literal>config.services.minio.accessKey</literal>. + ''; + }; + + minioAccessSecret = mkOption { + type = types.str; + description = '' + The value of the Minio access secret. + It is required in order to connect to the server. + By default this uses the one from the local minio server if enabled + and <literal>config.services.minio.secretKey</literal>. + ''; + }; + + minioBucketStats = mkOption { + type = types.bool; + default = false; + description = '' + Collect statistics about the buckets and files in buckets. + It requires more computation, use it carefully in case of large buckets.. + ''; + }; + }; + serviceOpts = { + serviceConfig = { + DynamicUser = true; + ExecStart = '' + ${pkgs.prometheus-minio-exporter}/bin/minio-exporter \ + -web.listen-address ${cfg.listenAddress}:${toString cfg.port} \ + -minio.server ${cfg.minioAddress} \ + -minio.access-key ${cfg.minioAccessKey} \ + -minio.access-secret ${cfg.minioAccessSecret} \ + ${optionalString cfg.minioBucketStats "-minio.bucket-stats"} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + }; + }; +} diff --git a/nixos/modules/services/monitoring/prometheus/exporters/nginx.nix b/nixos/modules/services/monitoring/prometheus/exporters/nginx.nix new file mode 100644 index 000000000000..6a3ba2d0457c --- /dev/null +++ b/nixos/modules/services/monitoring/prometheus/exporters/nginx.nix @@ -0,0 +1,31 @@ +{ config, lib, pkgs }: + +with lib; + +let + cfg = config.services.prometheus.exporters.nginx; +in +{ + port = 9113; + extraOpts = { + scrapeUri = mkOption { + type = types.string; + default = "http://localhost/nginx_status"; + description = '' + Address to access the nginx status page. + Can be enabled with services.nginx.statusPage = true. + ''; + }; + }; + serviceOpts = { + serviceConfig = { + DynamicUser = true; + ExecStart = '' + ${pkgs.prometheus-nginx-exporter}/bin/nginx_exporter \ + -nginx.scrape_uri '${cfg.scrapeUri}' \ + -telemetry.address ${cfg.listenAddress}:${toString cfg.port} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + }; + }; +} diff --git a/nixos/modules/services/monitoring/prometheus/exporters/node.nix b/nixos/modules/services/monitoring/prometheus/exporters/node.nix new file mode 100644 index 000000000000..c85f5f9cfb2d --- /dev/null +++ b/nixos/modules/services/monitoring/prometheus/exporters/node.nix @@ -0,0 +1,39 @@ +{ config, lib, pkgs }: + +with lib; + +let + cfg = config.services.prometheus.exporters.node; +in +{ + port = 9100; + extraOpts = { + enabledCollectors = mkOption { + type = types.listOf types.string; + default = []; + example = ''[ "systemd" ]''; + description = '' + Collectors to enable. The collectors listed here are enabled in addition to the default ones. + ''; + }; + disabledCollectors = mkOption { + type = types.listOf types.str; + default = []; + example = ''[ "timex" ]''; + description = '' + Collectors to disable which are enabled by default. + ''; + }; + }; + serviceOpts = { + serviceConfig = { + ExecStart = '' + ${pkgs.prometheus-node-exporter}/bin/node_exporter \ + ${concatMapStringsSep " " (x: "--collector." + x) cfg.enabledCollectors} \ + ${concatMapStringsSep " " (x: "--no-collector." + x) cfg.disabledCollectors} \ + --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + }; + }; +} diff --git a/nixos/modules/services/monitoring/prometheus/exporters/postfix.nix b/nixos/modules/services/monitoring/prometheus/exporters/postfix.nix new file mode 100644 index 000000000000..efe78ebcba86 --- /dev/null +++ b/nixos/modules/services/monitoring/prometheus/exporters/postfix.nix @@ -0,0 +1,81 @@ +{ config, lib, pkgs }: + +with lib; + +let + cfg = config.services.prometheus.exporters.postfix; +in +{ + port = 9154; + extraOpts = { + telemetryPath = mkOption { + type = types.str; + default = "/metrics"; + description = '' + Path under which to expose metrics. + ''; + }; + logfilePath = mkOption { + type = types.path; + default = "/var/log/postfix_exporter_input.log"; + example = "/var/log/mail.log"; + description = '' + Path where Postfix writes log entries. + This file will be truncated by this exporter! + ''; + }; + showqPath = mkOption { + type = types.path; + default = "/var/spool/postfix/public/showq"; + example = "/var/lib/postfix/queue/public/showq"; + description = '' + Path where Postfix places it's showq socket. + ''; + }; + systemd = { + enable = mkEnableOption '' + reading metrics from the systemd-journal instead of from a logfile + ''; + unit = mkOption { + type = types.str; + default = "postfix.service"; + description = '' + Name of the postfix systemd unit. + ''; + }; + slice = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + Name of the postfix systemd slice. + This overrides the <option>systemd.unit</option>. + ''; + }; + journalPath = mkOption { + type = types.nullOr types.path; + default = null; + description = '' + Path to the systemd journal. + ''; + }; + }; + }; + serviceOpts = { + serviceConfig = { + ExecStart = '' + ${pkgs.prometheus-postfix-exporter}/bin/postfix_exporter \ + --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \ + --web.telemetry-path ${cfg.telemetryPath} \ + --postfix.showq_path ${cfg.showqPath} \ + ${concatStringsSep " \\\n " (cfg.extraFlags + ++ optional cfg.systemd.enable "--systemd.enable" + ++ optional cfg.systemd.enable (if cfg.systemd.slice != null + then "--systemd.slice ${cfg.systemd.slice}" + else "--systemd.unit ${cfg.systemd.unit}") + ++ optional (cfg.systemd.enable && (cfg.systemd.journalPath != null)) + "--systemd.jounal_path ${cfg.systemd.journalPath}" + ++ optional (!cfg.systemd.enable) "--postfix.logfile_path ${cfg.logfilePath}")} + ''; + }; + }; +} diff --git a/nixos/modules/services/monitoring/prometheus/exporters/snmp.nix b/nixos/modules/services/monitoring/prometheus/exporters/snmp.nix new file mode 100644 index 000000000000..404cd0a1896b --- /dev/null +++ b/nixos/modules/services/monitoring/prometheus/exporters/snmp.nix @@ -0,0 +1,71 @@ +{ config, lib, pkgs }: + +with lib; + +let + cfg = config.services.prometheus.exporters.snmp; +in +{ + port = 9116; + extraOpts = { + configurationPath = mkOption { + type = types.nullOr types.path; + default = null; + description = '' + Path to a snmp exporter configuration file. Mutually exclusive with 'configuration' option. + ''; + example = "./snmp.yml"; + }; + + configuration = mkOption { + type = types.nullOr types.attrs; + default = {}; + description = '' + Snmp exporter configuration as nix attribute set. Mutually exclusive with 'configurationPath' option. + ''; + example = '' + { + "default" = { + "version" = 2; + "auth" = { + "community" = "public"; + }; + }; + }; + ''; + }; + + logFormat = mkOption { + type = types.str; + default = "logger:stderr"; + description = '' + Set the log target and format. + ''; + }; + + logLevel = mkOption { + type = types.enum ["debug" "info" "warn" "error" "fatal"]; + default = "info"; + description = '' + Only log messages with the given severity or above. + ''; + }; + }; + serviceOpts = let + configFile = if cfg.configurationPath != null + then cfg.configurationPath + else "${pkgs.writeText "snmp-eporter-conf.yml" (builtins.toJSON cfg.configuration)}"; + in { + serviceConfig = { + 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} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + }; + }; +} diff --git a/nixos/modules/services/monitoring/prometheus/exporters/unifi.nix b/nixos/modules/services/monitoring/prometheus/exporters/unifi.nix new file mode 100644 index 000000000000..011dcbe208e4 --- /dev/null +++ b/nixos/modules/services/monitoring/prometheus/exporters/unifi.nix @@ -0,0 +1,67 @@ +{ config, lib, pkgs }: + +with lib; + +let + cfg = config.services.prometheus.exporters.unifi; +in +{ + port = 9130; + extraOpts = { + unifiAddress = mkOption { + type = types.str; + example = "https://10.0.0.1:8443"; + description = '' + URL of the UniFi Controller API. + ''; + }; + + unifiInsecure = mkOption { + type = types.bool; + default = false; + description = '' + If enabled skip the verification of the TLS certificate of the UniFi Controller API. + Use with caution. + ''; + }; + + unifiUsername = mkOption { + type = types.str; + example = "ReadOnlyUser"; + description = '' + username for authentication against UniFi Controller API. + ''; + }; + + unifiPassword = mkOption { + type = types.str; + description = '' + Password for authentication against UniFi Controller API. + ''; + }; + + unifiTimeout = mkOption { + type = types.str; + default = "5s"; + example = "2m"; + description = '' + Timeout including unit for UniFi Controller API requests. + ''; + }; + }; + serviceOpts = { + serviceConfig = { + DynamicUser = true; + ExecStart = '' + ${pkgs.prometheus-unifi-exporter}/bin/unifi_exporter \ + -telemetry.addr ${cfg.listenAddress}:${toString cfg.port} \ + -unifi.addr ${cfg.unifiAddress} \ + -unifi.username ${cfg.unifiUsername} \ + -unifi.password ${cfg.unifiPassword} \ + -unifi.timeout ${cfg.unifiTimeout} \ + ${optionalString cfg.unifiInsecure "-unifi.insecure" } \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + }; + }; +} diff --git a/nixos/modules/services/monitoring/prometheus/exporters/varnish.nix b/nixos/modules/services/monitoring/prometheus/exporters/varnish.nix new file mode 100644 index 000000000000..b439a83e7aa2 --- /dev/null +++ b/nixos/modules/services/monitoring/prometheus/exporters/varnish.nix @@ -0,0 +1,21 @@ +{ config, lib, pkgs }: + +with lib; + +let + cfg = config.services.prometheus.exporters.varnish; +in +{ + port = 9131; + serviceOpts = { + path = [ pkgs.varnish ]; + serviceConfig = { + DynamicUser = true; + ExecStart = '' + ${pkgs.prometheus-varnish-exporter}/bin/prometheus_varnish_exporter \ + -web.listen-address ${cfg.listenAddress}:${toString cfg.port} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + }; + }; +} diff --git a/nixos/modules/services/monitoring/prometheus/fritzbox-exporter.nix b/nixos/modules/services/monitoring/prometheus/fritzbox-exporter.nix deleted file mode 100644 index 6da39b6519cb..000000000000 --- a/nixos/modules/services/monitoring/prometheus/fritzbox-exporter.nix +++ /dev/null @@ -1,76 +0,0 @@ -{ config, pkgs, lib, ... }: - -with lib; - -let - cfg = config.services.prometheus.fritzboxExporter; -in { - options = { - services.prometheus.fritzboxExporter = { - enable = mkEnableOption "prometheus fritzbox exporter"; - - port = mkOption { - type = types.int; - default = 9133; - description = '' - Port to listen on. - ''; - }; - - gatewayAddress = mkOption { - type = types.str; - default = "fritz.box"; - description = '' - The hostname or IP of the FRITZ!Box. - ''; - }; - - gatewayPort = mkOption { - type = types.int; - default = 49000; - description = '' - The port of the FRITZ!Box UPnP service. - ''; - }; - - extraFlags = mkOption { - type = types.listOf types.str; - default = []; - description = '' - Extra commandline options when launching the fritzbox exporter. - ''; - }; - - openFirewall = mkOption { - type = types.bool; - default = false; - description = '' - Open port in firewall for incoming connections. - ''; - }; - }; - }; - - config = mkIf cfg.enable { - networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port; - - systemd.services.prometheus-fritzbox-exporter = { - description = "Prometheus exporter for FRITZ!Box via UPnP"; - unitConfig.Documentation = "https://github.com/ndecker/fritzbox_exporter"; - wantedBy = [ "multi-user.target" ]; - serviceConfig = { - User = "nobody"; - Restart = "always"; - PrivateTmp = true; - WorkingDirectory = /tmp; - ExecStart = '' - ${pkgs.prometheus-fritzbox-exporter}/bin/fritzbox_exporter \ - -listen-address :${toString cfg.port} \ - -gateway-address ${cfg.gatewayAddress} \ - -gateway-port ${toString cfg.gatewayPort} \ - ${concatStringsSep " \\\n " cfg.extraFlags} - ''; - }; - }; - }; -} diff --git a/nixos/modules/services/monitoring/prometheus/json-exporter.nix b/nixos/modules/services/monitoring/prometheus/json-exporter.nix deleted file mode 100644 index 6bc56df9834b..000000000000 --- a/nixos/modules/services/monitoring/prometheus/json-exporter.nix +++ /dev/null @@ -1,74 +0,0 @@ -{ config, pkgs, lib, ... }: - -with lib; - -let - cfg = config.services.prometheus.jsonExporter; -in { - options = { - services.prometheus.jsonExporter = { - enable = mkEnableOption "prometheus JSON exporter"; - - url = mkOption { - type = types.str; - description = '' - URL to scrape JSON from. - ''; - }; - - configFile = mkOption { - type = types.path; - description = '' - Path to configuration file. - ''; - }; - - port = mkOption { - type = types.int; - default = 7979; - description = '' - Port to listen on. - ''; - }; - - extraFlags = mkOption { - type = types.listOf types.str; - default = []; - description = '' - Extra commandline options when launching the JSON exporter. - ''; - }; - - openFirewall = mkOption { - type = types.bool; - default = false; - description = '' - Open port in firewall for incoming connections. - ''; - }; - }; - }; - - config = mkIf cfg.enable { - networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port; - - systemd.services.prometheus-json-exporter = { - description = "Prometheus exporter for JSON over HTTP"; - unitConfig.Documentation = "https://github.com/kawamuray/prometheus-json-exporter"; - wantedBy = [ "multi-user.target" ]; - serviceConfig = { - User = "nobody"; - Restart = "always"; - PrivateTmp = true; - WorkingDirectory = /tmp; - ExecStart = '' - ${pkgs.prometheus-json-exporter}/bin/prometheus-json-exporter \ - --port ${toString cfg.port} \ - ${cfg.url} ${cfg.configFile} \ - ${concatStringsSep " \\\n " cfg.extraFlags} - ''; - ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; - }; - }; - }; -} diff --git a/nixos/modules/services/monitoring/prometheus/minio-exporter.nix b/nixos/modules/services/monitoring/prometheus/minio-exporter.nix deleted file mode 100644 index 4314671523cf..000000000000 --- a/nixos/modules/services/monitoring/prometheus/minio-exporter.nix +++ /dev/null @@ -1,117 +0,0 @@ -{ config, pkgs, lib, ... }: - -with lib; - -let - cfg = config.services.prometheus.minioExporter; -in { - options = { - services.prometheus.minioExporter = { - enable = mkEnableOption "prometheus minio exporter"; - - port = mkOption { - type = types.int; - default = 9290; - description = '' - Port to listen on. - ''; - }; - - listenAddress = mkOption { - type = types.nullOr types.str; - default = null; - example = "0.0.0.0"; - description = '' - Address to listen on for web interface and telemetry. - ''; - }; - - minioAddress = mkOption { - type = types.str; - example = "https://10.0.0.1:9000"; - default = if config.services.minio.enable then "http://localhost:9000" else null; - description = '' - The URL of the minio server. - Use HTTPS if Minio accepts secure connections only. - By default this connects to the local minio server if enabled. - ''; - }; - - minioAccessKey = mkOption ({ - type = types.str; - example = "BKIKJAA5BMMU2RHO6IBB"; - description = '' - The value of the Minio access key. - It is required in order to connect to the server. - By default this uses the one from the local minio server if enabled - and <literal>config.services.minio.accessKey</literal>. - ''; - } // optionalAttrs (config.services.minio.enable && config.services.minio.accessKey != "") { - default = config.services.minio.accessKey; - }); - - minioAccessSecret = mkOption ({ - type = types.str; - description = '' - The calue of the Minio access secret. - It is required in order to connect to the server. - By default this uses the one from the local minio server if enabled - and <literal>config.services.minio.secretKey</literal>. - ''; - } // optionalAttrs (config.services.minio.enable && config.services.minio.secretKey != "") { - default = config.services.minio.secretKey; - }); - - minioBucketStats = mkOption { - type = types.bool; - default = false; - description = '' - Collect statistics about the buckets and files in buckets. - It requires more computation, use it carefully in case of large buckets.. - ''; - }; - - extraFlags = mkOption { - type = types.listOf types.str; - default = []; - description = '' - Extra commandline options when launching the minio exporter. - ''; - }; - - openFirewall = mkOption { - type = types.bool; - default = false; - description = '' - Open port in firewall for incoming connections. - ''; - }; - }; - }; - - config = mkIf cfg.enable { - networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port; - - systemd.services.prometheus-minio-exporter = { - description = "Prometheus exporter for Minio server metrics"; - unitConfig.Documentation = "https://github.com/joe-pll/minio-exporter"; - wantedBy = [ "multi-user.target" ]; - after = optional config.services.minio.enable "minio.service"; - serviceConfig = { - DynamicUser = true; - Restart = "always"; - PrivateTmp = true; - WorkingDirectory = /tmp; - ExecStart = '' - ${pkgs.prometheus-minio-exporter}/bin/minio-exporter \ - -web.listen-address ${optionalString (cfg.listenAddress != null) cfg.listenAddress}:${toString cfg.port} \ - -minio.server ${cfg.minioAddress} \ - -minio.access-key ${cfg.minioAccessKey} \ - -minio.access-secret ${cfg.minioAccessSecret} \ - ${optionalString cfg.minioBucketStats "-minio.bucket-stats"} \ - ${concatStringsSep " \\\n " cfg.extraFlags} - ''; - }; - }; - }; -} diff --git a/nixos/modules/services/monitoring/prometheus/nginx-exporter.nix b/nixos/modules/services/monitoring/prometheus/nginx-exporter.nix deleted file mode 100644 index 1ccafee3b18b..000000000000 --- a/nixos/modules/services/monitoring/prometheus/nginx-exporter.nix +++ /dev/null @@ -1,78 +0,0 @@ -{ config, pkgs, lib, ... }: - -with lib; - -let - cfg = config.services.prometheus.nginxExporter; -in { - options = { - services.prometheus.nginxExporter = { - enable = mkEnableOption "prometheus nginx exporter"; - - port = mkOption { - type = types.int; - default = 9113; - description = '' - Port to listen on. - ''; - }; - - listenAddress = mkOption { - type = types.string; - default = "0.0.0.0"; - description = '' - Address to listen on. - ''; - }; - - scrapeUri = mkOption { - type = types.string; - default = "http://localhost/nginx_status"; - description = '' - Address to access the nginx status page. - Can be enabled with services.nginx.statusPage = true. - ''; - }; - - extraFlags = mkOption { - type = types.listOf types.str; - default = []; - description = '' - Extra commandline options when launching the nginx exporter. - ''; - }; - - openFirewall = mkOption { - type = types.bool; - default = false; - description = '' - Open port in firewall for incoming connections. - ''; - }; - }; - }; - - config = mkIf cfg.enable { - networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port; - - systemd.services.prometheus-nginx-exporter = { - after = [ "network.target" "nginx.service" ]; - description = "Prometheus exporter for nginx metrics"; - unitConfig.Documentation = "https://github.com/discordianfish/nginx_exporter"; - wantedBy = [ "multi-user.target" ]; - serviceConfig = { - User = "nobody"; - Restart = "always"; - PrivateTmp = true; - WorkingDirectory = /tmp; - ExecStart = '' - ${pkgs.prometheus-nginx-exporter}/bin/nginx_exporter \ - -nginx.scrape_uri '${cfg.scrapeUri}' \ - -telemetry.address ${cfg.listenAddress}:${toString cfg.port} \ - ${concatStringsSep " \\\n " cfg.extraFlags} - ''; - ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; - }; - }; - }; -} diff --git a/nixos/modules/services/monitoring/prometheus/node-exporter.nix b/nixos/modules/services/monitoring/prometheus/node-exporter.nix deleted file mode 100644 index bad4389ce799..000000000000 --- a/nixos/modules/services/monitoring/prometheus/node-exporter.nix +++ /dev/null @@ -1,87 +0,0 @@ -{ config, pkgs, lib, ... }: - -with lib; - -let - cfg = config.services.prometheus.nodeExporter; -in { - options = { - services.prometheus.nodeExporter = { - enable = mkEnableOption "prometheus node exporter"; - - port = mkOption { - type = types.int; - default = 9100; - description = '' - Port to listen on. - ''; - }; - - listenAddress = mkOption { - type = types.string; - default = "0.0.0.0"; - description = '' - Address to listen on. - ''; - }; - - enabledCollectors = mkOption { - type = types.listOf types.string; - default = []; - example = ''[ "systemd" ]''; - description = '' - Collectors to enable. The collectors listed here are enabled in addition to the default ones. - ''; - }; - - disabledCollectors = mkOption { - type = types.listOf types.str; - default = []; - example = ''[ "timex" ]''; - description = '' - Collectors to disable which are enabled by default. - ''; - }; - - extraFlags = mkOption { - type = types.listOf types.str; - default = []; - description = '' - Extra commandline options when launching the node exporter. - ''; - }; - - openFirewall = mkOption { - type = types.bool; - default = false; - description = '' - Open port in firewall for incoming connections. - ''; - }; - }; - }; - - config = mkIf cfg.enable { - networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port; - - systemd.services.prometheus-node-exporter = { - description = "Prometheus exporter for machine metrics"; - unitConfig.Documentation = "https://github.com/prometheus/node_exporter"; - wantedBy = [ "multi-user.target" ]; - script = '' - exec ${pkgs.prometheus-node-exporter}/bin/node_exporter \ - ${concatMapStringsSep " " (x: "--collector." + x) cfg.enabledCollectors} \ - ${concatMapStringsSep " " (x: "--no-collector." + x) cfg.disabledCollectors} \ - --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \ - ${concatStringsSep " \\\n " cfg.extraFlags} - ''; - serviceConfig = { - User = "nobody"; - Restart = "always"; - PrivateTmp = true; - WorkingDirectory = /tmp; - ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; - }; - }; - }; -} diff --git a/nixos/modules/services/monitoring/prometheus/snmp-exporter.nix b/nixos/modules/services/monitoring/prometheus/snmp-exporter.nix deleted file mode 100644 index fe33f8c1f04d..000000000000 --- a/nixos/modules/services/monitoring/prometheus/snmp-exporter.nix +++ /dev/null @@ -1,127 +0,0 @@ -{ config, pkgs, lib, ... }: - -with lib; - -let - cfg = config.services.prometheus.snmpExporter; - mkConfigFile = pkgs.writeText "snmp.yml" (if cfg.configurationPath == null then builtins.toJSON cfg.configuration else builtins.readFile cfg.configurationPath); -in { - options = { - services.prometheus.snmpExporter = { - enable = mkEnableOption "Prometheus snmp exporter"; - - user = mkOption { - type = types.str; - default = "nobody"; - description = '' - User name under which snmp exporter shall be run. - ''; - }; - - group = mkOption { - type = types.str; - default = "nogroup"; - description = '' - Group under which snmp exporter shall be run. - ''; - }; - - port = mkOption { - type = types.int; - default = 9116; - description = '' - Port to listen on. - ''; - }; - - listenAddress = mkOption { - type = types.nullOr types.str; - default = null; - description = '' - Address to listen on for web interface and telemetry. - ''; - }; - - configurationPath = mkOption { - type = types.nullOr types.path; - default = null; - description = '' - Path to a snmp exporter configuration file. Mutually exclusive with 'configuration' option. - ''; - example = "./snmp.yml"; - }; - - configuration = mkOption { - type = types.nullOr types.attrs; - default = {}; - description = '' - Snmp exporter configuration as nix attribute set. Mutually exclusive with 'configurationPath' option. - ''; - example = '' - { - "default" = { - "version" = 2; - "auth" = { - "community" = "public"; - }; - }; - }; - ''; - }; - - logFormat = mkOption { - type = types.str; - default = "logger:stderr"; - description = '' - Set the log target and format. - ''; - }; - - logLevel = mkOption { - type = types.enum ["debug" "info" "warn" "error" "fatal"]; - default = "info"; - description = '' - Only log messages with the given severity or above. - ''; - }; - - openFirewall = mkOption { - type = types.bool; - default = false; - description = '' - Open port in firewall for incoming connections. - ''; - }; - }; - }; - - config = mkIf cfg.enable { - networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port; - - assertions = singleton - { - assertion = (cfg.configurationPath == null) != (cfg.configuration == null); - message = "Please ensure you have either 'configuration' or 'configurationPath' set!"; - }; - - systemd.services.prometheus-snmp-exporter = { - wantedBy = [ "multi-user.target" ]; - after = [ "network.target" ]; - script = '' - ${pkgs.prometheus-snmp-exporter.bin}/bin/snmp_exporter \ - -config.file ${mkConfigFile} \ - -log.format ${cfg.logFormat} \ - -log.level ${cfg.logLevel} \ - -web.listen-address ${optionalString (cfg.listenAddress != null) cfg.listenAddress}:${toString cfg.port} - ''; - - serviceConfig = { - User = cfg.user; - Group = cfg.group; - Restart = "always"; - PrivateTmp = true; - WorkingDirectory = "/tmp"; - }; - }; - }; -} diff --git a/nixos/modules/services/monitoring/prometheus/unifi-exporter.nix b/nixos/modules/services/monitoring/prometheus/unifi-exporter.nix deleted file mode 100644 index 0a56d6ae95a5..000000000000 --- a/nixos/modules/services/monitoring/prometheus/unifi-exporter.nix +++ /dev/null @@ -1,105 +0,0 @@ -{ config, pkgs, lib, ... }: - -with lib; - -let - cfg = config.services.prometheus.unifiExporter; -in { - options = { - services.prometheus.unifiExporter = { - enable = mkEnableOption "prometheus unifi exporter"; - - port = mkOption { - type = types.int; - default = 9130; - description = '' - Port to listen on. - ''; - }; - - unifiAddress = mkOption { - type = types.str; - example = "https://10.0.0.1:8443"; - description = '' - URL of the UniFi Controller API. - ''; - }; - - unifiInsecure = mkOption { - type = types.bool; - default = false; - description = '' - If enabled skip the verification of the TLS certificate of the UniFi Controller API. - Use with caution. - ''; - }; - - unifiUsername = mkOption { - type = types.str; - example = "ReadOnlyUser"; - description = '' - username for authentication against UniFi Controller API. - ''; - }; - - unifiPassword = mkOption { - type = types.str; - description = '' - Password for authentication against UniFi Controller API. - ''; - }; - - unifiTimeout = mkOption { - type = types.str; - default = "5s"; - example = "2m"; - description = '' - Timeout including unit for UniFi Controller API requests. - ''; - }; - - extraFlags = mkOption { - type = types.listOf types.str; - default = []; - description = '' - Extra commandline options when launching the unifi exporter. - ''; - }; - - openFirewall = mkOption { - type = types.bool; - default = false; - description = '' - Open port in firewall for incoming connections. - ''; - }; - }; - }; - - config = mkIf cfg.enable { - networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port; - - systemd.services.prometheus-unifi-exporter = { - description = "Prometheus exporter for UniFi Controller metrics"; - unitConfig.Documentation = "https://github.com/mdlayher/unifi_exporter"; - wantedBy = [ "multi-user.target" ]; - after = optional config.services.unifi.enable "unifi.service"; - serviceConfig = { - User = "nobody"; - Restart = "always"; - PrivateTmp = true; - WorkingDirectory = /tmp; - ExecStart = '' - ${pkgs.prometheus-unifi-exporter}/bin/unifi_exporter \ - -telemetry.addr :${toString cfg.port} \ - -unifi.addr ${cfg.unifiAddress} \ - -unifi.username ${cfg.unifiUsername} \ - -unifi.password ${cfg.unifiPassword} \ - -unifi.timeout ${cfg.unifiTimeout} \ - ${optionalString cfg.unifiInsecure "-unifi.insecure" } \ - ${concatStringsSep " \\\n " cfg.extraFlags} - ''; - }; - }; - }; -} diff --git a/nixos/modules/services/monitoring/prometheus/varnish-exporter.nix b/nixos/modules/services/monitoring/prometheus/varnish-exporter.nix deleted file mode 100644 index 143ebb62aeac..000000000000 --- a/nixos/modules/services/monitoring/prometheus/varnish-exporter.nix +++ /dev/null @@ -1,61 +0,0 @@ -{ config, pkgs, lib, ... }: - -# Shamelessly cribbed from nginx-exporter.nix. ~ C. -with lib; - -let - cfg = config.services.prometheus.varnishExporter; -in { - options = { - services.prometheus.varnishExporter = { - enable = mkEnableOption "prometheus Varnish exporter"; - - port = mkOption { - type = types.int; - default = 9131; - description = '' - Port to listen on. - ''; - }; - - extraFlags = mkOption { - type = types.listOf types.str; - default = []; - description = '' - Extra commandline options when launching the Varnish exporter. - ''; - }; - - openFirewall = mkOption { - type = types.bool; - default = false; - description = '' - Open port in firewall for incoming connections. - ''; - }; - }; - }; - - config = mkIf cfg.enable { - networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port; - - systemd.services.prometheus-varnish-exporter = { - description = "Prometheus exporter for Varnish metrics"; - unitConfig.Documentation = "https://github.com/jonnenauha/prometheus_varnish_exporter"; - wantedBy = [ "multi-user.target" ]; - path = [ pkgs.varnish ]; - script = '' - exec ${pkgs.prometheus-varnish-exporter}/bin/prometheus_varnish_exporter \ - -web.listen-address :${toString cfg.port} \ - ${concatStringsSep " \\\n " cfg.extraFlags} - ''; - serviceConfig = { - User = "nobody"; - Restart = "always"; - PrivateTmp = true; - WorkingDirectory = /tmp; - ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; - }; - }; - }; -} diff --git a/nixos/modules/services/monitoring/smartd.nix b/nixos/modules/services/monitoring/smartd.nix index b8d9e58a5a82..fecae4ca1b36 100644 --- a/nixos/modules/services/monitoring/smartd.nix +++ b/nixos/modules/services/monitoring/smartd.nix @@ -14,7 +14,7 @@ let nx = cfg.notifications.x11; smartdNotify = pkgs.writeScript "smartd-notify.sh" '' - #! ${pkgs.stdenv.shell} + #! ${pkgs.runtimeShell} ${optionalString nm.enable '' { ${pkgs.coreutils}/bin/cat << EOF diff --git a/nixos/modules/services/network-filesystems/xtreemfs.nix b/nixos/modules/services/network-filesystems/xtreemfs.nix index 0c6714563d8a..95d7641e8b53 100644 --- a/nixos/modules/services/network-filesystems/xtreemfs.nix +++ b/nixos/modules/services/network-filesystems/xtreemfs.nix @@ -11,7 +11,7 @@ let home = cfg.homeDir; startupScript = class: configPath: pkgs.writeScript "xtreemfs-osd.sh" '' - #! ${pkgs.stdenv.shell} + #! ${pkgs.runtimeShell} JAVA_HOME="${pkgs.jdk}" JAVADIR="${xtreemfs}/share/java" JAVA_CALL="$JAVA_HOME/bin/java -ea -cp $JAVADIR/XtreemFS.jar:$JAVADIR/BabuDB.jar:$JAVADIR/Flease.jar:$JAVADIR/protobuf-java-2.5.0.jar:$JAVADIR/Foundation.jar:$JAVADIR/jdmkrt.jar:$JAVADIR/jdmktk.jar:$JAVADIR/commons-codec-1.3.jar" diff --git a/nixos/modules/services/network-filesystems/yandex-disk.nix b/nixos/modules/services/network-filesystems/yandex-disk.nix index 4de206641331..44b0edf62018 100644 --- a/nixos/modules/services/network-filesystems/yandex-disk.nix +++ b/nixos/modules/services/network-filesystems/yandex-disk.nix @@ -99,10 +99,10 @@ in exit 1 fi - ${pkgs.su}/bin/su -s ${pkgs.stdenv.shell} ${u} \ + ${pkgs.su}/bin/su -s ${pkgs.runtimeShell} ${u} \ -c '${pkgs.yandex-disk}/bin/yandex-disk token -p ${cfg.password} ${cfg.username} ${dir}/token' - ${pkgs.su}/bin/su -s ${pkgs.stdenv.shell} ${u} \ + ${pkgs.su}/bin/su -s ${pkgs.runtimeShell} ${u} \ -c '${pkgs.yandex-disk}/bin/yandex-disk start --no-daemon -a ${dir}/token -d ${cfg.directory} --exclude-dirs=${cfg.excludes}' ''; diff --git a/nixos/modules/services/networking/amuled.nix b/nixos/modules/services/networking/amuled.nix index fc7d56a24fa7..9898f164c5cf 100644 --- a/nixos/modules/services/networking/amuled.nix +++ b/nixos/modules/services/networking/amuled.nix @@ -68,7 +68,7 @@ in ''; script = '' - ${pkgs.su}/bin/su -s ${pkgs.stdenv.shell} ${user} \ + ${pkgs.su}/bin/su -s ${pkgs.runtimeShell} ${user} \ -c 'HOME="${cfg.dataDir}" ${pkgs.amuleDaemon}/bin/amuled' ''; }; diff --git a/nixos/modules/services/networking/dante.nix b/nixos/modules/services/networking/dante.nix index 32acce51e692..20d4faa1cdb1 100644 --- a/nixos/modules/services/networking/dante.nix +++ b/nixos/modules/services/networking/dante.nix @@ -6,6 +6,7 @@ let confFile = pkgs.writeText "dante-sockd.conf" '' user.privileged: root user.unprivileged: dante + logoutput: syslog ${cfg.config} ''; @@ -21,11 +22,10 @@ in enable = mkEnableOption "Dante SOCKS proxy"; config = mkOption { - default = null; - type = types.nullOr types.str; + type = types.lines; description = '' - Contents of Dante's configuration file - NOTE: user.privileged/user.unprivileged are set by the service + Contents of Dante's configuration file. + NOTE: user.privileged, user.unprivileged and logoutput are set by the service. ''; }; }; @@ -33,7 +33,7 @@ in config = mkIf cfg.enable { assertions = [ - { assertion = cfg.config != null; + { assertion = cfg.config != ""; message = "please provide Dante configuration file contents"; } ]; @@ -54,7 +54,8 @@ in Type = "simple"; ExecStart = "${pkgs.dante}/bin/sockd -f ${confFile}"; ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; - Restart = "always"; + # Can crash sometimes; see https://github.com/NixOS/nixpkgs/pull/39005#issuecomment-381828708 + Restart = "on-failure"; }; }; }; diff --git a/nixos/modules/services/networking/ddclient.nix b/nixos/modules/services/networking/ddclient.nix index 9e56545f746c..9a2e13e9553c 100644 --- a/nixos/modules/services/networking/ddclient.nix +++ b/nixos/modules/services/networking/ddclient.nix @@ -3,24 +3,24 @@ let cfg = config.services.ddclient; boolToStr = bool: if bool then "yes" else "no"; + dataDir = "/var/lib/ddclient"; configText = '' # This file can be used as a template for configFile or is automatically generated by Nix options. - daemon=${toString cfg.interval} - cache=${cfg.homeDir}/ddclient.cache - pid=/run/ddclient/ddclient.pid - foreground=NO + cache=${dataDir}/ddclient.cache + foreground=YES use=${cfg.use} login=${cfg.username} password=${cfg.password} protocol=${cfg.protocol} - ${let server = cfg.server; in - lib.optionalString (server != "") "server=${server}"} + ${lib.optionalString (cfg.script != "") "script=${cfg.script}"} + ${lib.optionalString (cfg.server != "") "server=${cfg.server}"} + ${lib.optionalString (cfg.zone != "") "zone=${cfg.zone}"} ssl=${boolToStr cfg.ssl} wildcard=YES quiet=${boolToStr cfg.quiet} verbose=${boolToStr cfg.verbose} - ${cfg.domain} + ${lib.concatStringsSep "," cfg.domains} ${cfg.extraConfig} ''; @@ -44,17 +44,11 @@ with lib; ''; }; - homeDir = mkOption { - default = "/var/lib/ddclient"; - type = str; - description = "Home directory for the daemon user."; - }; - - domain = mkOption { - default = ""; - type = str; + domains = mkOption { + default = [ "" ]; + type = listOf str; description = '' - Domain name to synchronize. + Domain name(s) to synchronize. ''; }; @@ -62,7 +56,7 @@ with lib; default = ""; type = str; description = '' - Username. + User name. ''; }; @@ -75,9 +69,12 @@ with lib; }; interval = mkOption { - default = 600; - type = int; - description = "The interval at which to run the check and update."; + default = "10min"; + type = str; + description = '' + The interval at which to run the check and update. + See <command>man 7 systemd.time</command> for the format. + ''; }; configFile = mkOption { @@ -95,7 +92,7 @@ with lib; default = "dyndns2"; type = str; description = '' - Protocol to use with dynamic DNS provider (see http://sourceforge.net/apps/trac/ddclient/wiki/Protocols). + Protocol to use with dynamic DNS provider (see https://sourceforge.net/p/ddclient/wiki/protocols). ''; }; @@ -115,11 +112,20 @@ with lib; ''; }; - extraConfig = mkOption { + + quiet = mkOption { + default = false; + type = bool; + description = '' + Print no messages for unnecessary updates. + ''; + }; + + script = mkOption { default = ""; - type = lines; + type = str; description = '' - Extra configuration. Contents will be added verbatim to the configuration file. + script as required by some providers. ''; }; @@ -139,11 +145,19 @@ with lib; ''; }; - quiet = mkOption { - default = false; - type = bool; + zone = mkOption { + default = ""; + type = str; description = '' - Print no messages for unnecessary updates. + zone as required by some providers. + ''; + }; + + extraConfig = mkOption { + default = ""; + type = lines; + description = '' + Extra configuration. Contents will be added verbatim to the configuration file. ''; }; }; @@ -153,23 +167,8 @@ with lib; ###### implementation config = mkIf config.services.ddclient.enable { - - users = { - extraGroups.ddclient.gid = config.ids.gids.ddclient; - - extraUsers.ddclient = { - uid = config.ids.uids.ddclient; - description = "ddclient daemon user"; - group = "ddclient"; - home = cfg.homeDir; - createHome = true; - }; - }; - environment.etc."ddclient.conf" = { enable = cfg.configFile == "/etc/ddclient.conf"; - uid = config.ids.uids.ddclient; - gid = config.ids.gids.ddclient; mode = "0600"; text = configText; }; @@ -180,15 +179,22 @@ with lib; after = [ "network.target" ]; restartTriggers = [ config.environment.etc."ddclient.conf".source ]; - serviceConfig = { - RuntimeDirectory = "ddclient"; - # we cannot run in forking mode as it swallows all the program output - Type = "simple"; - User = "ddclient"; - Group = "ddclient"; - ExecStart = "${lib.getBin pkgs.ddclient}/bin/ddclient -foreground -file ${cfg.configFile}"; - ProtectSystem = "full"; - PrivateTmp = true; + serviceConfig = rec { + DynamicUser = true; + RuntimeDirectory = StateDirectory; + StateDirectory = builtins.baseNameOf dataDir; + Type = "oneshot"; + ExecStartPre = "!${lib.getBin pkgs.coreutils}/bin/install -m666 ${cfg.configFile} /run/${RuntimeDirectory}/ddclient.conf"; + ExecStart = "${lib.getBin pkgs.ddclient}/bin/ddclient -file /run/${RuntimeDirectory}/ddclient.conf"; + }; + }; + + systemd.timers.ddclient = { + description = "Run ddclient"; + wantedBy = [ "timers.target" ]; + timerConfig = { + OnBootSec = cfg.interval; + OnUnitInactiveSec = cfg.interval; }; }; }; diff --git a/nixos/modules/services/networking/dhcpd.nix b/nixos/modules/services/networking/dhcpd.nix index 2eac6dfec5b7..fd7e317eee95 100644 --- a/nixos/modules/services/networking/dhcpd.nix +++ b/nixos/modules/services/networking/dhcpd.nix @@ -36,6 +36,7 @@ let preStart = '' mkdir -m 755 -p ${cfg.stateDir} + chown dhcpd:nogroup ${cfg.stateDir} touch ${cfg.stateDir}/dhcpd.leases ''; diff --git a/nixos/modules/services/networking/dnscache.nix b/nixos/modules/services/networking/dnscache.nix index 379203cd1ab6..ba5c8e2d5e53 100644 --- a/nixos/modules/services/networking/dnscache.nix +++ b/nixos/modules/services/networking/dnscache.nix @@ -9,12 +9,12 @@ let mkdir -p $out/{servers,ip} ${concatMapStrings (ip: '' - echo > "$out/ip/"${lib.escapeShellArg ip} + touch "$out/ip/"${lib.escapeShellArg ip} '') cfg.clientIps} ${concatStrings (mapAttrsToList (host: ips: '' ${concatMapStrings (ip: '' - echo ${lib.escapeShellArg ip} > "$out/servers/"${lib.escapeShellArg host} + echo ${lib.escapeShellArg ip} >> "$out/servers/"${lib.escapeShellArg host} '') ips} '') cfg.domainServers)} @@ -34,33 +34,49 @@ in { options = { services.dnscache = { + enable = mkOption { default = false; type = types.bool; - description = "Whether to run the dnscache caching dns server"; + description = "Whether to run the dnscache caching dns server."; }; ip = mkOption { default = "0.0.0.0"; type = types.str; - description = "IP address on which to listen for connections"; + description = "IP address on which to listen for connections."; }; clientIps = mkOption { default = [ "127.0.0.1" ]; type = types.listOf types.str; - description = "client IP addresses (or prefixes) from which to accept connections"; + description = "Client IP addresses (or prefixes) from which to accept connections."; example = ["192.168" "172.23.75.82"]; }; domainServers = mkOption { default = { }; type = types.attrsOf (types.listOf types.str); - description = "table of {hostname: server} pairs to use as authoritative servers for hosts (and subhosts)"; + description = '' + Table of {hostname: server} pairs to use as authoritative servers for hosts (and subhosts). + If entry for @ is not specified predefined list of root servers is used. + ''; example = { - "example.com" = ["8.8.8.8" "8.8.4.4"]; + "@" = ["8.8.8.8" "8.8.4.4"]; + "example.com" = ["192.168.100.100"]; }; }; + + forwardOnly = mkOption { + default = false; + type = types.bool; + description = '' + Whether to treat root servers (for @) as caching + servers, requesting addresses the same way a client does. This is + needed if you want to use e.g. Google DNS as your upstream DNS. + ''; + }; + }; }; @@ -82,6 +98,7 @@ in { ''; script = '' cd /var/lib/dnscache/ + ${optionalString cfg.forwardOnly "export FORWARDONLY=1"} exec ./run ''; }; diff --git a/nixos/modules/services/networking/firewall.nix b/nixos/modules/services/networking/firewall.nix index bce48c8f65e5..20c0b0acf165 100644 --- a/nixos/modules/services/networking/firewall.nix +++ b/nixos/modules/services/networking/firewall.nix @@ -54,7 +54,7 @@ let ''; writeShScript = name: text: let dir = pkgs.writeScriptBin name '' - #! ${pkgs.stdenv.shell} -e + #! ${pkgs.runtimeShell} -e ${text} ''; in "${dir}/bin/${name}"; diff --git a/nixos/modules/services/networking/flashpolicyd.nix b/nixos/modules/services/networking/flashpolicyd.nix index 5ba85178179b..5b83ce131389 100644 --- a/nixos/modules/services/networking/flashpolicyd.nix +++ b/nixos/modules/services/networking/flashpolicyd.nix @@ -22,7 +22,7 @@ let flashpolicydWrapper = pkgs.writeScriptBin "flashpolicyd" '' - #! ${pkgs.stdenv.shell} + #! ${pkgs.runtimeShell} exec ${flashpolicyd}/Perl_xinetd/in.flashpolicyd.pl \ --file=${pkgs.writeText "flashpolixy.xml" cfg.policy} \ 2> /dev/null diff --git a/nixos/modules/services/networking/hans.nix b/nixos/modules/services/networking/hans.nix new file mode 100644 index 000000000000..dd34ef8d4ca1 --- /dev/null +++ b/nixos/modules/services/networking/hans.nix @@ -0,0 +1,145 @@ +# NixOS module for hans, ip over icmp daemon + +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.hans; + + hansUser = "hans"; + +in +{ + + ### configuration + + options = { + + services.hans = { + clients = mkOption { + default = {}; + description = '' + Each attribute of this option defines a systemd service that + runs hans. Many or none may be defined. + The name of each service is + <literal>hans-<replaceable>name</replaceable></literal> + where <replaceable>name</replaceable> is the name of the + corresponding attribute name. + ''; + example = literalExample '' + { + foo = { + server = "192.0.2.1"; + extraConfig = "-v"; + } + } + ''; + type = types.attrsOf (types.submodule ( + { + options = { + server = mkOption { + type = types.str; + default = ""; + description = "IP address of server running hans"; + example = "192.0.2.1"; + }; + + extraConfig = mkOption { + type = types.str; + default = ""; + description = "Additional command line parameters"; + example = "-v"; + }; + + passwordFile = mkOption { + type = types.str; + default = ""; + description = "File that containts password"; + }; + + }; + })); + }; + + server = { + enable = mkOption { + type = types.bool; + default = false; + description = "enable hans server"; + }; + + ip = mkOption { + type = types.str; + default = ""; + description = "The assigned ip range"; + example = "198.51.100.0"; + }; + + respondToSystemPings = mkOption { + type = types.bool; + default = false; + description = "Force hans respond to ordinary pings"; + }; + + extraConfig = mkOption { + type = types.str; + default = ""; + description = "Additional command line parameters"; + example = "-v"; + }; + + passwordFile = mkOption { + type = types.str; + default = ""; + description = "File that containts password"; + }; + }; + + }; + }; + + ### implementation + + config = mkIf (cfg.server.enable || cfg.clients != {}) { + boot.kernel.sysctl = optionalAttrs cfg.server.respondToSystemPings { + "net.ipv4.icmp_echo_ignore_all" = 1; + }; + + boot.kernelModules = [ "tun" ]; + + systemd.services = + let + createHansClientService = name: cfg: + { + description = "hans client - ${name}"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + script = "${pkgs.hans}/bin/hans -f -u ${hansUser} ${cfg.extraConfig} -c ${cfg.server} ${optionalString (cfg.passwordFile != "") "-p $(cat \"${cfg.passwordFile}\")"}"; + serviceConfig = { + RestartSec = "30s"; + Restart = "always"; + }; + }; + in + listToAttrs ( + mapAttrsToList + (name: value: nameValuePair "hans-${name}" (createHansClientService name value)) + cfg.clients + ) // { + hans = mkIf (cfg.server.enable) { + description = "hans, ip over icmp server daemon"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + script = "${pkgs.hans}/bin/hans -f -u ${hansUser} ${cfg.server.extraConfig} -s ${cfg.server.ip} ${optionalString cfg.server.respondToSystemPings "-r"} ${optionalString (cfg.server.passwordFile != "") "-p $(cat \"${cfg.server.passwordFile}\")"}"; + }; + }; + + users.extraUsers = singleton { + name = hansUser; + description = "Hans daemon user"; + }; + }; + + meta.maintainers = with maintainers; [ gnidorah ]; +} diff --git a/nixos/modules/services/networking/iodine.nix b/nixos/modules/services/networking/iodine.nix index 512dbd77ae4b..3f41421d27f7 100644 --- a/nixos/modules/services/networking/iodine.nix +++ b/nixos/modules/services/networking/iodine.nix @@ -32,7 +32,7 @@ in foo = { server = "tunnel.mdomain.com"; relay = "8.8.8.8"; - extraConfig = "-P mysecurepassword"; + extraConfig = "-v"; } } ''; @@ -57,7 +57,13 @@ in type = types.str; default = ""; description = "Additional command line parameters"; - example = "-P mysecurepassword -l 192.168.1.10 -p 23"; + example = "-l 192.168.1.10 -p 23"; + }; + + passwordFile = mkOption { + type = types.str; + default = ""; + description = "File that containts password"; }; }; })); @@ -88,7 +94,13 @@ in type = types.str; default = ""; description = "Additional command line parameters"; - example = "-P mysecurepassword -l 192.168.1.10 -p 23"; + example = "-l 192.168.1.10 -p 23"; + }; + + passwordFile = mkOption { + type = types.str; + default = ""; + description = "File that containts password"; }; }; @@ -108,10 +120,10 @@ in description = "iodine client - ${name}"; after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; + script = "${pkgs.iodine}/bin/iodine -f -u ${iodinedUser} ${cfg.extraConfig} ${optionalString (cfg.passwordFile != "") "-P $(cat \"${cfg.passwordFile}\")"} ${cfg.relay} ${cfg.server}"; serviceConfig = { RestartSec = "30s"; Restart = "always"; - ExecStart = "${pkgs.iodine}/bin/iodine -f -u ${iodinedUser} ${cfg.extraConfig} ${cfg.relay} ${cfg.server}"; }; }; in @@ -124,7 +136,7 @@ in description = "iodine, ip over dns server daemon"; after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; - serviceConfig.ExecStart = "${pkgs.iodine}/bin/iodined -f -u ${iodinedUser} ${cfg.server.extraConfig} ${cfg.server.ip} ${cfg.server.domain}"; + script = "${pkgs.iodine}/bin/iodined -f -u ${iodinedUser} ${cfg.server.extraConfig} ${optionalString (cfg.server.passwordFile != "") "-P $(cat \"${cfg.server.passwordFile}\")"} ${cfg.server.ip} ${cfg.server.domain}"; }; }; diff --git a/nixos/modules/services/networking/iwd.nix b/nixos/modules/services/networking/iwd.nix index 23787bce9911..344212ad8329 100644 --- a/nixos/modules/services/networking/iwd.nix +++ b/nixos/modules/services/networking/iwd.nix @@ -26,7 +26,7 @@ in { wants = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; - serviceConfig.ExecStart = "${pkgs.iwd}/bin/iwd"; + serviceConfig.ExecStart = "${pkgs.iwd}/libexec/iwd"; }; }; diff --git a/nixos/modules/services/networking/murmur.nix b/nixos/modules/services/networking/murmur.nix index 13d7c3254f9d..873d62dbf341 100644 --- a/nixos/modules/services/networking/murmur.nix +++ b/nixos/modules/services/networking/murmur.nix @@ -80,7 +80,7 @@ in pidfile = mkOption { type = types.path; - default = "/tmp/murmurd.pid"; + default = "/run/murmur/murmurd.pid"; description = "Path to PID file for Murmur daemon."; }; @@ -252,6 +252,7 @@ in serviceConfig = { Type = "forking"; + RuntimeDirectory = "murmur"; PIDFile = cfg.pidfile; Restart = "always"; User = "murmur"; diff --git a/nixos/modules/services/networking/networkmanager.nix b/nixos/modules/services/networking/networkmanager.nix index f83fb7a6d5dc..10e96eb40362 100644 --- a/nixos/modules/services/networking/networkmanager.nix +++ b/nixos/modules/services/networking/networkmanager.nix @@ -135,8 +135,7 @@ in { default = { inherit networkmanager modemmanager wpa_supplicant networkmanager-openvpn networkmanager-vpnc networkmanager-openconnect networkmanager-fortisslvpn - networkmanager-pptp networkmanager-l2tp - networkmanager-iodine; }; + networkmanager-l2tp networkmanager-iodine; }; internal = true; }; @@ -267,8 +266,6 @@ in { message = "You can not use networking.networkmanager with networking.wireless"; }]; - boot.kernelModules = [ "ppp_mppe" ]; # Needed for most (all?) PPTP VPN connections. - environment.etc = with cfg.basePackages; [ { source = configFile; target = "NetworkManager/NetworkManager.conf"; @@ -285,9 +282,6 @@ in { { source = "${networkmanager-fortisslvpn}/etc/NetworkManager/VPN/nm-fortisslvpn-service.name"; target = "NetworkManager/VPN/nm-fortisslvpn-service.name"; } - { source = "${networkmanager-pptp}/etc/NetworkManager/VPN/nm-pptp-service.name"; - target = "NetworkManager/VPN/nm-pptp-service.name"; - } { source = "${networkmanager-l2tp}/etc/NetworkManager/VPN/nm-l2tp-service.name"; target = "NetworkManager/VPN/nm-l2tp-service.name"; } diff --git a/nixos/modules/services/networking/nftables.nix b/nixos/modules/services/networking/nftables.nix index 56b942054140..ad7c013a5449 100644 --- a/nixos/modules/services/networking/nftables.nix +++ b/nixos/modules/services/networking/nftables.nix @@ -116,7 +116,7 @@ in include "${cfg.rulesetFile}" ''; checkScript = pkgs.writeScript "nftables-check" '' - #! ${pkgs.stdenv.shell} -e + #! ${pkgs.runtimeShell} -e if $(${pkgs.kmod}/bin/lsmod | grep -q ip_tables); then echo "Unload ip_tables before using nftables!" 1>&2 exit 1 diff --git a/nixos/modules/services/networking/nsd.nix b/nixos/modules/services/networking/nsd.nix index 4241e6fcceab..0b52b1d3e302 100644 --- a/nixos/modules/services/networking/nsd.nix +++ b/nixos/modules/services/networking/nsd.nix @@ -250,6 +250,46 @@ let Use imports or pkgs.lib.readFile if you don't want this data in your config file. ''; }; + + dnssec = mkEnableOption "DNSSEC"; + + dnssecPolicy = { + algorithm = mkOption { + type = types.str; + default = "RSASHA256"; + description = "Which algorithm to use for DNSSEC"; + }; + keyttl = mkOption { + type = types.str; + default = "1h"; + description = "TTL for dnssec records"; + }; + coverage = mkOption { + type = types.str; + default = "1y"; + description = '' + The length of time to ensure that keys will be correct; no action will be taken to create new keys to be activated after this time. + ''; + }; + zsk = mkOption { + type = keyPolicy; + default = { keySize = 2048; + prePublish = "1w"; + postPublish = "1w"; + rollPeriod = "1mo"; + }; + description = "Key policy for zone signing keys"; + }; + ksk = mkOption { + type = keyPolicy; + default = { keySize = 4096; + prePublish = "1mo"; + postPublish = "1mo"; + rollPeriod = "0"; + }; + description = "Key policy for key signing keys"; + }; + }; maxRefreshSecs = mkOption { type = types.nullOr types.int; @@ -367,10 +407,61 @@ let and stats_noreset. ''; }; + }; + }; + keyPolicy = types.submodule { + options = { + keySize = mkOption { + type = types.int; + description = "Key size in bits"; + }; + prePublish = mkOption { + type = types.str; + description = "How long in advance to publish new keys"; + }; + postPublish = mkOption { + type = types.str; + description = "How long after deactivation to keep a key in the zone"; + }; + rollPeriod = mkOption { + type = types.str; + description = "How frequently to change keys"; + }; }; }; + dnssecZones = (filterAttrs (n: v: if v ? dnssec then v.dnssec else false) zoneConfigs); + + dnssec = length (attrNames dnssecZones) != 0; + + signZones = optionalString dnssec '' + mkdir -p ${stateDir}/dnssec + chown ${username}:${username} ${stateDir}/dnssec + chmod 0600 ${stateDir}/dnssec + + ${concatStrings (mapAttrsToList signZone dnssecZones)} + ''; + signZone = name: zone: '' + ${pkgs.bind}/bin/dnssec-keymgr -g ${pkgs.bind}/bin/dnssec-keygen -s ${pkgs.bind}/bin/dnssec-settime -K ${stateDir}/dnssec -c ${policyFile name zone.dnssecPolicy} ${name} + ${pkgs.bind}/bin/dnssec-signzone -S -K ${stateDir}/dnssec -o ${name} -O full -N date ${stateDir}/zones/${name} + ${nsdPkg}/sbin/nsd-checkzone ${name} ${stateDir}/zones/${name}.signed && mv -v ${stateDir}/zones/${name}.signed ${stateDir}/zones/${name} + ''; + policyFile = name: policy: pkgs.writeText "${name}.policy" '' + zone ${name} { + algorithm ${policy.algorithm}; + key-size zsk ${toString policy.zsk.keySize}; + key-size ksk ${toString policy.ksk.keySize}; + keyttl ${policy.keyttl}; + pre-publish zsk ${policy.zsk.prePublish}; + pre-publish ksk ${policy.ksk.prePublish}; + post-publish zsk ${policy.zsk.postPublish}; + post-publish ksk ${policy.ksk.postPublish}; + roll-period zsk ${policy.zsk.rollPeriod}; + roll-period ksk ${policy.ksk.rollPeriod}; + coverage ${policy.coverage}; + }; + ''; in { # options are ordered alphanumerically @@ -380,6 +471,14 @@ in bind8Stats = mkEnableOption "BIND8 like statistics"; + dnssecInterval = mkOption { + type = types.str; + default = "1h"; + description = '' + How often to check whether dnssec key rollover is required + ''; + }; + extraConfig = mkOption { type = types.str; default = ""; @@ -741,7 +840,6 @@ in }; - zones = mkOption { type = types.attrsOf zoneOptions; default = {}; @@ -785,7 +883,6 @@ in serverGroup1. ''; }; - }; config = mkIf cfg.enable { @@ -832,9 +929,9 @@ in mkdir -m 0700 -p "${stateDir}/var" cat > "${stateDir}/don't touch anything in here" << EOF - Everything in this directory except NSD's state in var is - automatically generated and will be purged and redeployed - by the nsd.service pre-start script. + Everything in this directory except NSD's state in var and dnssec + is automatically generated and will be purged and redeployed by + the nsd.service pre-start script. EOF chown ${username}:${username} -R "${stateDir}/private" @@ -848,6 +945,34 @@ in ''; }; + nixpkgs.config = mkIf dnssec { + bind.enablePython = true; + }; + + systemd.timers."nsd-dnssec" = mkIf dnssec { + description = "Automatic DNSSEC key rollover"; + + wantedBy = [ "nsd.service" ]; + + timerConfig = { + OnActiveSec = cfg.dnssecInterval; + OnUnitActiveSec = cfg.dnssecInterval; + }; + }; + + systemd.services."nsd-dnssec" = mkIf dnssec { + description = "DNSSEC key rollover"; + + wantedBy = [ "nsd.service" ]; + before = [ "nsd.service" ]; + + script = signZones; + + postStop = '' + ${pkgs.systemd}/bin/systemctl kill -s SIGHUP nsd.service + ''; + }; + }; meta.maintainers = with lib.maintainers; [ hrdinka ]; diff --git a/nixos/modules/services/networking/openvpn.nix b/nixos/modules/services/networking/openvpn.nix index 7a96b673c51e..a418839d22b8 100644 --- a/nixos/modules/services/networking/openvpn.nix +++ b/nixos/modules/services/networking/openvpn.nix @@ -65,7 +65,7 @@ let path = [ pkgs.iptables pkgs.iproute pkgs.nettools ]; - serviceConfig.ExecStart = "@${openvpn}/sbin/openvpn openvpn --config ${configFile}"; + serviceConfig.ExecStart = "@${openvpn}/sbin/openvpn openvpn --suppress-timestamps --config ${configFile}"; serviceConfig.Restart = "always"; serviceConfig.Type = "notify"; }; diff --git a/nixos/modules/services/networking/prosody.nix b/nixos/modules/services/networking/prosody.nix index 9d7e6d6018af..1b4f81f6b56e 100644 --- a/nixos/modules/services/networking/prosody.nix +++ b/nixos/modules/services/networking/prosody.nix @@ -15,6 +15,7 @@ let description = "Path to the key file."; }; + # TODO: rename to certificate to match the prosody config cert = mkOption { type = types.path; description = "Path to the certificate file."; @@ -30,7 +31,7 @@ let }; moduleOpts = { - + # Generally required roster = mkOption { type = types.bool; default = true; @@ -61,12 +62,38 @@ let description = "Service discovery"; }; - legacyauth = mkOption { + # Not essential, but recommended + carbons = mkOption { type = types.bool; default = true; - description = "Legacy authentication. Only used by some old clients and bots"; + description = "Keep multiple clients in sync"; + }; + + pep = mkOption { + type = types.bool; + default = true; + description = "Enables users to publish their mood, activity, playing music and more"; + }; + + private = mkOption { + type = types.bool; + default = true; + description = "Private XML storage (for room bookmarks, etc.)"; + }; + + blocklist = mkOption { + type = types.bool; + default = true; + description = "Allow users to block communications with other users"; }; + vcard = mkOption { + type = types.bool; + default = true; + description = "Allow users to set vCards"; + }; + + # Nice to have version = mkOption { type = types.bool; default = true; @@ -91,36 +118,112 @@ let description = "Replies to XMPP pings with pongs"; }; - console = mkOption { + register = mkOption { + type = types.bool; + default = true; + description = "Allow users to register on this server using a client and change passwords"; + }; + + mam = mkOption { type = types.bool; default = false; - description = "telnet to port 5582"; + description = "Store messages in an archive and allow users to access it"; }; + # Admin interfaces + admin_adhoc = mkOption { + type = types.bool; + default = true; + description = "Allows administration via an XMPP client that supports ad-hoc commands"; + }; + + admin_telnet = mkOption { + type = types.bool; + default = false; + description = "Opens telnet console interface on localhost port 5582"; + }; + + # HTTP modules bosh = mkOption { type = types.bool; default = false; description = "Enable BOSH clients, aka 'Jabber over HTTP'"; }; - httpserver = mkOption { + websocket = mkOption { + type = types.bool; + default = false; + description = "Enable WebSocket support"; + }; + + http_files = mkOption { type = types.bool; default = false; description = "Serve static files from a directory over HTTP"; }; - websocket = mkOption { + # Other specific functionality + limits = mkOption { type = types.bool; default = false; - description = "Enable WebSocket support"; + description = "Enable bandwidth limiting for XMPP connections"; + }; + + groups = mkOption { + type = types.bool; + default = false; + description = "Shared roster support"; + }; + + server_contact_info = mkOption { + type = types.bool; + default = false; + description = "Publish contact information for this service"; + }; + + announce = mkOption { + type = types.bool; + default = false; + description = "Send announcement to all online users"; + }; + + welcome = mkOption { + type = types.bool; + default = false; + description = "Welcome users who register accounts"; + }; + + watchregistrations = mkOption { + type = types.bool; + default = false; + description = "Alert admins of registrations"; + }; + + motd = mkOption { + type = types.bool; + default = false; + description = "Send a message to users when they log in"; + }; + + legacyauth = mkOption { + type = types.bool; + default = false; + description = "Legacy authentication. Only used by some old clients and bots"; + }; + + proxy65 = mkOption { + type = types.bool; + default = false; + description = "Enables a file transfer proxy service which clients behind NAT can use"; }; }; toLua = x: if builtins.isString x then ''"${x}"'' - else if builtins.isBool x then toString x + else if builtins.isBool x then (if x == true then "true" else "false") else if builtins.isInt x then toString x + else if builtins.isList x then ''{ ${lib.concatStringsSep ", " (map (n: toLua n) x) } }'' else throw "Invalid Lua value"; createSSLOptsStr = o: '' @@ -192,12 +295,83 @@ in ''; }; + dataDir = mkOption { + type = types.string; + description = "Directory where Prosody stores its data"; + default = "/var/lib/prosody"; + }; + + user = mkOption { + type = types.str; + default = "prosody"; + description = "User account under which prosody runs."; + }; + + group = mkOption { + type = types.str; + default = "prosody"; + description = "Group account under which prosody runs."; + }; + allowRegistration = mkOption { type = types.bool; default = false; description = "Allow account creation"; }; + c2sRequireEncryption = mkOption { + type = types.bool; + default = true; + description = '' + Force clients to use encrypted connections? This option will + prevent clients from authenticating unless they are using encryption. + ''; + }; + + s2sRequireEncryption = mkOption { + type = types.bool; + default = true; + description = '' + Force servers to use encrypted connections? This option will + prevent servers from authenticating unless they are using encryption. + Note that this is different from authentication. + ''; + }; + + s2sSecureAuth = mkOption { + type = types.bool; + default = false; + description = '' + Force certificate authentication for server-to-server connections? + This provides ideal security, but requires servers you communicate + with to support encryption AND present valid, trusted certificates. + For more information see https://prosody.im/doc/s2s#security + ''; + }; + + s2sInsecureDomains = mkOption { + type = types.listOf types.str; + default = []; + example = [ "insecure.example.com" ]; + description = '' + Some servers have invalid or self-signed certificates. You can list + remote domains here that will not be required to authenticate using + certificates. They will be authenticated using DNS instead, even + when s2s_secure_auth is enabled. + ''; + }; + + s2sSecureDomains = mkOption { + type = types.listOf types.str; + default = []; + example = [ "jabber.org" ]; + description = '' + Even if you leave s2s_secure_auth disabled, you can still require valid + certificates for some domains by specifying a list here. + ''; + }; + + modules = moduleOpts; extraModules = mkOption { @@ -206,6 +380,12 @@ in description = "Enable custom modules"; }; + extraPluginPaths = mkOption { + type = types.listOf types.path; + default = []; + description = "Addtional path in which to look find plugins/modules"; + }; + virtualHosts = mkOption { description = "Define the virtual hosts"; @@ -255,37 +435,47 @@ in config = mkIf cfg.enable { - environment.systemPackages = [ pkgs.prosody ]; + environment.systemPackages = [ cfg.package ]; environment.etc."prosody/prosody.cfg.lua".text = '' - pidfile = "/var/lib/prosody/prosody.pid" - + pidfile = "/run/prosody/prosody.pid" log = "*syslog" - data_path = "/var/lib/prosody" - - allow_registration = ${boolToString cfg.allowRegistration}; - - ${ optionalString cfg.modules.console "console_enabled = true;" } + data_path = "${cfg.dataDir}" + plugin_paths = { + ${lib.concatStringsSep ", " (map (n: "\"${n}\"") cfg.extraPluginPaths) } + } ${ optionalString (cfg.ssl != null) (createSSLOptsStr cfg.ssl) } - admins = { ${lib.concatStringsSep ", " (map (n: "\"${n}\"") cfg.admins) } }; + admins = ${toLua cfg.admins} + + -- we already build with libevent, so we can just enable it for a more performant server + use_libevent = true modules_enabled = { ${ lib.concatStringsSep "\n\ \ " (lib.mapAttrsToList - (name: val: optionalString val ''"${name}";'') + (name: val: optionalString val "${toLua name};") cfg.modules) } + ${ lib.concatStringsSep "\n" (map (x: "${toLua x};") cfg.package.communityModules)} + ${ lib.concatStringsSep "\n" (map (x: "${toLua x};") cfg.extraModules)} + }; - ${ optionalString cfg.allowRegistration "\"register\"\;" } + allow_registration = ${toLua cfg.allowRegistration} - ${ lib.concatStringsSep "\n" (map (x: "\"${x}\";") cfg.extraModules)} + c2s_require_encryption = ${toLua cfg.c2sRequireEncryption} + + s2s_require_encryption = ${toLua cfg.s2sRequireEncryption} + + s2s_secure_auth = ${toLua cfg.s2sSecureAuth} + + s2s_insecure_domains = ${toLua cfg.s2sInsecureDomains} + + s2s_secure_domains = ${toLua cfg.s2sSecureDomains} - "posix"; - }; ${ cfg.extraConfig } @@ -297,15 +487,15 @@ in '') cfg.virtualHosts) } ''; - users.extraUsers.prosody = { + users.extraUsers.prosody = mkIf (cfg.user == "prosody") { uid = config.ids.uids.prosody; description = "Prosody user"; createHome = true; - group = "prosody"; - home = "/var/lib/prosody"; + inherit (cfg) group; + home = "${cfg.dataDir}"; }; - users.extraGroups.prosody = { + users.extraGroups.prosody = mkIf (cfg.group == "prosody") { gid = config.ids.gids.prosody; }; @@ -316,9 +506,11 @@ in wantedBy = [ "multi-user.target" ]; restartTriggers = [ config.environment.etc."prosody/prosody.cfg.lua".source ]; serviceConfig = { - User = "prosody"; + User = cfg.user; + Group = cfg.group; Type = "forking"; - PIDFile = "/var/lib/prosody/prosody.pid"; + RuntimeDirectory = [ "prosody" ]; + PIDFile = "/run/prosody/prosody.pid"; ExecStart = "${cfg.package}/bin/prosodyctl start"; }; }; diff --git a/nixos/modules/services/networking/quagga.nix b/nixos/modules/services/networking/quagga.nix index aab58cc77b90..22204e53203c 100644 --- a/nixos/modules/services/networking/quagga.nix +++ b/nixos/modules/services/networking/quagga.nix @@ -133,7 +133,7 @@ in users.groups = { quagga = {}; # Members of the quaggavty group can use vtysh to inspect the Quagga daemons - quaggavty = {}; + quaggavty = { members = [ "quagga" ]; }; }; systemd.services = diff --git a/nixos/modules/services/networking/rdnssd.nix b/nixos/modules/services/networking/rdnssd.nix index 95833d31e99d..a102242eae71 100644 --- a/nixos/modules/services/networking/rdnssd.nix +++ b/nixos/modules/services/networking/rdnssd.nix @@ -6,7 +6,7 @@ with lib; let mergeHook = pkgs.writeScript "rdnssd-merge-hook" '' - #! ${pkgs.stdenv.shell} -e + #! ${pkgs.runtimeShell} -e ${pkgs.openresolv}/bin/resolvconf -u ''; in diff --git a/nixos/modules/services/networking/resilio.nix b/nixos/modules/services/networking/resilio.nix index d1c4101f80bd..2956a5ecbc04 100644 --- a/nixos/modules/services/networking/resilio.nix +++ b/nixos/modules/services/networking/resilio.nix @@ -50,12 +50,7 @@ in description = '' If enabled, start the Resilio Sync daemon. Once enabled, you can interact with the service through the Web UI, or configure it in your - NixOS configuration. Enabling the <literal>resilio</literal> service - also installs a systemd user unit which can be used to start - user-specific copies of the daemon. Once installed, you can use - <literal>systemctl --user start resilio</literal> as your user to start - the daemon using the configuration file located at - <literal>$HOME/.config/resilio-sync/config.json</literal>. + NixOS configuration. ''; }; diff --git a/nixos/modules/services/networking/shadowsocks.nix b/nixos/modules/services/networking/shadowsocks.nix new file mode 100644 index 000000000000..fe6d65a5f963 --- /dev/null +++ b/nixos/modules/services/networking/shadowsocks.nix @@ -0,0 +1,112 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.shadowsocks; + + opts = { + server = cfg.localAddress; + server_port = cfg.port; + method = cfg.encryptionMethod; + mode = cfg.mode; + user = "nobody"; + fast_open = true; + } // optionalAttrs (cfg.password != null) { password = cfg.password; }; + + configFile = pkgs.writeText "shadowsocks.json" (builtins.toJSON opts); + +in + +{ + + ###### interface + + options = { + + services.shadowsocks = { + + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to run shadowsocks-libev shadowsocks server. + ''; + }; + + localAddress = mkOption { + type = types.str; + default = "0.0.0.0"; + description = '' + Local address to which the server binds. + ''; + }; + + port = mkOption { + type = types.int; + default = 8388; + description = '' + Port which the server uses. + ''; + }; + + password = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + Password for connecting clients. + ''; + }; + + passwordFile = mkOption { + type = types.nullOr types.path; + default = null; + description = '' + Password file with a password for connecting clients. + ''; + }; + + mode = mkOption { + type = types.enum [ "tcp_only" "tcp_and_udp" "udp_only" ]; + default = "tcp_and_udp"; + description = '' + Relay protocols. + ''; + }; + + encryptionMethod = mkOption { + type = types.str; + default = "chacha20-ietf-poly1305"; + description = '' + Encryption method. See <link xlink:href="https://github.com/shadowsocks/shadowsocks-org/wiki/AEAD-Ciphers"/>. + ''; + }; + + }; + + }; + + + ###### implementation + + config = mkIf cfg.enable { + assertions = singleton + { assertion = cfg.password == null || cfg.passwordFile == null; + message = "Cannot use both password and passwordFile for shadowsocks-libev"; + }; + + systemd.services.shadowsocks-libev = { + description = "shadowsocks-libev Daemon"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + path = [ pkgs.shadowsocks-libev ] ++ optional (cfg.passwordFile != null) pkgs.jq; + serviceConfig.PrivateTmp = true; + script = '' + ${optionalString (cfg.passwordFile != null) '' + cat ${configFile} | jq --arg password "$(cat "${cfg.passwordFile}")" '. + { password: $password }' > /tmp/shadowsocks.json + ''} + exec ss-server -c ${if cfg.passwordFile != null then "/tmp/shadowsocks.json" else configFile} + ''; + }; + }; +} diff --git a/nixos/modules/services/networking/ssh/sshd.nix b/nixos/modules/services/networking/ssh/sshd.nix index e50c4dbacf36..aab1203086ce 100644 --- a/nixos/modules/services/networking/ssh/sshd.nix +++ b/nixos/modules/services/networking/ssh/sshd.nix @@ -213,6 +213,65 @@ in description = "Files from which authorized keys are read."; }; + kexAlgorithms = mkOption { + type = types.listOf types.str; + default = [ + "curve25519-sha256@libssh.org" + "diffie-hellman-group-exchange-sha256" + ]; + description = '' + Allowed key exchange algorithms + </para> + <para> + Defaults to recommended settings from both + <link xlink:href="https://stribika.github.io/2015/01/04/secure-secure-shell.html" /> + and + <link xlink:href="https://wiki.mozilla.org/Security/Guidelines/OpenSSH#Modern_.28OpenSSH_6.7.2B.29" /> + ''; + }; + + ciphers = mkOption { + type = types.listOf types.str; + default = [ + "chacha20-poly1305@openssh.com" + "aes256-gcm@openssh.com" + "aes128-gcm@openssh.com" + "aes256-ctr" + "aes192-ctr" + "aes128-ctr" + ]; + description = '' + Allowed ciphers + </para> + <para> + Defaults to recommended settings from both + <link xlink:href="https://stribika.github.io/2015/01/04/secure-secure-shell.html" /> + and + <link xlink:href="https://wiki.mozilla.org/Security/Guidelines/OpenSSH#Modern_.28OpenSSH_6.7.2B.29" /> + ''; + }; + + macs = mkOption { + type = types.listOf types.str; + default = [ + "hmac-sha2-512-etm@openssh.com" + "hmac-sha2-256-etm@openssh.com" + "umac-128-etm@openssh.com" + "hmac-sha2-512" + "hmac-sha2-256" + "umac-128@openssh.com" + ]; + description = '' + Allowed MACs + </para> + <para> + Defaults to recommended settings from both + <link xlink:href="https://stribika.github.io/2015/01/04/secure-secure-shell.html" /> + and + <link xlink:href="https://wiki.mozilla.org/Security/Guidelines/OpenSSH#Modern_.28OpenSSH_6.7.2B.29" /> + ''; + }; + extraConfig = mkOption { type = types.lines; default = ""; @@ -363,14 +422,9 @@ in HostKey ${k.path} '')} - ### Recommended settings from both: - # https://stribika.github.io/2015/01/04/secure-secure-shell.html - # and - # https://wiki.mozilla.org/Security/Guidelines/OpenSSH#Modern_.28OpenSSH_6.7.2B.29 - - KexAlgorithms curve25519-sha256@libssh.org,diffie-hellman-group-exchange-sha256 - Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr - MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-512,hmac-sha2-256,umac-128@openssh.com + KexAlgorithms ${concatStringsSep "," cfg.kexAlgorithms} + Ciphers ${concatStringsSep "," cfg.ciphers} + MACs ${concatStringsSep "," cfg.macs} # LogLevel VERBOSE logs user's key fingerprint on login. # Needed to have a clear audit track of which key was used to log in. diff --git a/nixos/modules/services/networking/strongswan-swanctl/module.nix b/nixos/modules/services/networking/strongswan-swanctl/module.nix new file mode 100644 index 000000000000..d770094960b2 --- /dev/null +++ b/nixos/modules/services/networking/strongswan-swanctl/module.nix @@ -0,0 +1,82 @@ +{ config, lib, pkgs, ... }: + +with lib; +with (import ./param-lib.nix lib); + +let + cfg = config.services.strongswan-swanctl; + swanctlParams = import ./swanctl-params.nix lib; +in { + options.services.strongswan-swanctl = { + enable = mkEnableOption "strongswan-swanctl service"; + + package = mkOption { + type = types.package; + default = pkgs.strongswan; + defaultText = "pkgs.strongswan"; + description = '' + The strongswan derivation to use. + ''; + }; + + strongswan.extraConfig = mkOption { + type = types.str; + default = ""; + description = '' + Contents of the <literal>strongswan.conf</literal> file. + ''; + }; + + swanctl = paramsToOptions swanctlParams; + }; + + config = mkIf cfg.enable { + + assertions = [ + { assertion = !config.services.strongswan.enable; + message = "cannot enable both services.strongswan and services.strongswan-swanctl. Choose either one."; + } + ]; + + environment.etc."swanctl/swanctl.conf".text = + paramsToConf cfg.swanctl swanctlParams; + + # The swanctl command complains when the following directories don't exist: + # See: https://wiki.strongswan.org/projects/strongswan/wiki/Swanctldirectory + system.activationScripts.strongswan-swanctl-etc = stringAfter ["etc"] '' + mkdir -p '/etc/swanctl/x509' # Trusted X.509 end entity certificates + mkdir -p '/etc/swanctl/x509ca' # Trusted X.509 Certificate Authority certificates + mkdir -p '/etc/swanctl/x509ocsp' + mkdir -p '/etc/swanctl/x509aa' # Trusted X.509 Attribute Authority certificates + mkdir -p '/etc/swanctl/x509ac' # Attribute Certificates + mkdir -p '/etc/swanctl/x509crl' # Certificate Revocation Lists + mkdir -p '/etc/swanctl/pubkey' # Raw public keys + mkdir -p '/etc/swanctl/private' # Private keys in any format + mkdir -p '/etc/swanctl/rsa' # PKCS#1 encoded RSA private keys + mkdir -p '/etc/swanctl/ecdsa' # Plain ECDSA private keys + mkdir -p '/etc/swanctl/bliss' + mkdir -p '/etc/swanctl/pkcs8' # PKCS#8 encoded private keys of any type + mkdir -p '/etc/swanctl/pkcs12' # PKCS#12 containers + ''; + + systemd.services.strongswan-swanctl = { + description = "strongSwan IPsec IKEv1/IKEv2 daemon using swanctl"; + wantedBy = [ "multi-user.target" ]; + after = [ "network-online.target" "keys.target" ]; + wants = [ "keys.target" ]; + path = with pkgs; [ kmod iproute iptables utillinux ]; + environment.STRONGSWAN_CONF = pkgs.writeTextFile { + name = "strongswan.conf"; + text = cfg.strongswan.extraConfig; + }; + restartTriggers = [ config.environment.etc."swanctl/swanctl.conf".source ]; + serviceConfig = { + ExecStart = "${cfg.package}/sbin/charon-systemd"; + Type = "notify"; + ExecStartPost = "${cfg.package}/sbin/swanctl --load-all --noprompt"; + ExecReload = "${cfg.package}/sbin/swanctl --reload"; + Restart = "on-abnormal"; + }; + }; + }; +} diff --git a/nixos/modules/services/networking/strongswan-swanctl/param-constructors.nix b/nixos/modules/services/networking/strongswan-swanctl/param-constructors.nix new file mode 100644 index 000000000000..5e74a96664f0 --- /dev/null +++ b/nixos/modules/services/networking/strongswan-swanctl/param-constructors.nix @@ -0,0 +1,162 @@ +# In the following context a parameter is an attribute set that +# contains a NixOS option and a render function. It also contains the +# attribute: '_type = "param"' so we can distinguish it from other +# sets. +# +# The render function is used to convert the value of the option to a +# snippet of strongswan.conf. Most parameters simply render their +# value to a string. For example, take the following parameter: +# +# threads = mkIntParam 10 "Threads to use for request handling."; +# +# When a users defines the corresponding option as for example: +# +# services.strongswan-swanctl.strongswan.threads = 32; +# +# It will get rendered to the following snippet in strongswan.conf: +# +# threads = 32 +# +# Some parameters however need to be able to change the attribute +# name. For example, take the following parameter: +# +# id = mkPrefixedAttrsOfParam (mkOptionalStrParam "") "..."; +# +# A user can define the corresponding option as for example: +# +# id = { +# "foo" = "bar"; +# "baz" = "qux"; +# }; +# +# This will get rendered to the following snippet: +# +# foo-id = bar +# baz-id = qux +# +# For this reason the render function is not simply a function from +# value -> string but a function from a value to an attribute set: +# { "${name}" = string }. This allows parameters to change the attribute +# name like in the previous example. + +lib : + +with lib; +with (import ./param-lib.nix lib); + +rec { + mkParamOfType = type : strongswanDefault : description : { + _type = "param"; + option = mkOption { + type = types.nullOr type; + default = null; + description = documentDefault description strongswanDefault; + }; + render = single toString; + }; + + documentDefault = description : strongswanDefault : + if isNull strongswanDefault + then description + else description + '' + </para><para> + StrongSwan default: <literal><![CDATA[${builtins.toJSON strongswanDefault}]]></literal> + ''; + + single = f: name: value: { "${name}" = f value; }; + + mkStrParam = mkParamOfType types.str; + mkOptionalStrParam = mkStrParam null; + + mkEnumParam = values : mkParamOfType (types.enum values); + + mkIntParam = mkParamOfType types.int; + mkOptionalIntParam = mkIntParam null; + + # We should have floats in Nix... + mkFloatParam = mkStrParam; + + # TODO: Check for hex format: + mkHexParam = mkStrParam; + mkOptionalHexParam = mkOptionalStrParam; + + # TODO: Check for duration format: + mkDurationParam = mkStrParam; + mkOptionalDurationParam = mkOptionalStrParam; + + mkYesNoParam = strongswanDefault : description : { + _type = "param"; + option = mkOption { + type = types.nullOr types.bool; + default = null; + description = documentDefault description strongswanDefault; + }; + render = single (b: if b then "yes" else "no"); + }; + yes = true; + no = false; + + mkSpaceSepListParam = mkSepListParam " "; + mkCommaSepListParam = mkSepListParam ","; + + mkSepListParam = sep : strongswanDefault : description : { + _type = "param"; + option = mkOption { + type = types.nullOr (types.listOf types.str); + default = null; + description = documentDefault description strongswanDefault; + }; + render = single (value: concatStringsSep sep value); + }; + + mkAttrsOfParams = params : + mkAttrsOf params (types.submodule {options = paramsToOptions params;}); + + mkAttrsOfParam = param : + mkAttrsOf param param.option.type; + + mkAttrsOf = param : option : description : { + _type = "param"; + option = mkOption { + type = types.attrsOf option; + default = {}; + inherit description; + }; + render = single (attrs: + (paramsToRenderedStrings attrs + (mapAttrs (_n: _v: param) attrs))); + }; + + mkPrefixedAttrsOfParams = params : + mkPrefixedAttrsOf params (types.submodule {options = paramsToOptions params;}); + + mkPrefixedAttrsOfParam = param : + mkPrefixedAttrsOf param param.option.type; + + mkPrefixedAttrsOf = p : option : description : { + _type = "param"; + option = mkOption { + type = types.attrsOf option; + default = {}; + inherit description; + }; + render = prefix: attrs: + let prefixedAttrs = mapAttrs' (name: nameValuePair "${prefix}-${name}") attrs; + in paramsToRenderedStrings prefixedAttrs + (mapAttrs (_n: _v: p) prefixedAttrs); + }; + + mkPostfixedAttrsOfParams = params : description : { + _type = "param"; + option = mkOption { + type = types.attrsOf (types.submodule {options = paramsToOptions params;}); + default = {}; + inherit description; + }; + render = postfix: attrs: + let postfixedAttrs = mapAttrs' (name: nameValuePair "${name}-${postfix}") attrs; + in paramsToRenderedStrings postfixedAttrs + (mapAttrs (_n: _v: params) postfixedAttrs); + }; + +} diff --git a/nixos/modules/services/networking/strongswan-swanctl/param-lib.nix b/nixos/modules/services/networking/strongswan-swanctl/param-lib.nix new file mode 100644 index 000000000000..fb87e81f3215 --- /dev/null +++ b/nixos/modules/services/networking/strongswan-swanctl/param-lib.nix @@ -0,0 +1,82 @@ +lib : + +with lib; + +rec { + paramsToConf = cfg : ps : mkConf 0 (paramsToRenderedStrings cfg ps); + + # mkConf takes an indentation level (which usually starts at 0) and a nested + # attribute set of strings and will render that set to a strongswan.conf style + # configuration format. For example: + # + # mkConf 0 {a = "1"; b = { c = { "foo" = "2"; "bar" = "3"; }; d = "4";};} => '' + # a = 1 + # b { + # c { + # foo = 2 + # bar = 3 + # } + # d = 4 + # }'' + mkConf = indent : ps : + concatMapStringsSep "\n" + (name: + let value = ps."${name}"; + indentation = replicate indent " "; + in + indentation + ( + if isAttrs value + then "${name} {\n" + + mkConf (indent + 2) value + "\n" + + indentation + "}" + else "${name} = ${value}" + ) + ) + (attrNames ps); + + replicate = n : c : concatStrings (builtins.genList (_x : c) n); + + # `paramsToRenderedStrings cfg ps` converts the NixOS configuration `cfg` + # (typically the "config" argument of a NixOS module) and the set of + # parameters `ps` (an attribute set where the values are constructed using the + # parameter constructors in ./param-constructors.nix) to a nested attribute + # set of strings (rendered parameters). + paramsToRenderedStrings = cfg : ps : + filterEmptySets ( + (mapParamsRecursive (path: name: param: + let value = attrByPath path null cfg; + in optionalAttrs (!isNull value) (param.render name value) + ) ps)); + + filterEmptySets = set : filterAttrs (n: v: !(isNull v)) (mapAttrs (name: value: + if isAttrs value + then let value' = filterEmptySets value; + in if value' == {} + then null + else value' + else value + ) set); + + # Recursively map over every parameter in the given attribute set. + mapParamsRecursive = mapAttrsRecursiveCond' (as: (!(as ? "_type" && as._type == "param"))); + + mapAttrsRecursiveCond' = cond: f: set: + let + recurse = path: set: + let + g = + name: value: + if isAttrs value && cond value + then { "${name}" = recurse (path ++ [name]) value; } + else f (path ++ [name]) name value; + in mapAttrs'' g set; + in recurse [] set; + + mapAttrs'' = f: set: + foldl' (a: b: a // b) {} (map (attr: f attr set.${attr}) (attrNames set)); + + # Extract the options from the given set of parameters. + paramsToOptions = ps : + mapParamsRecursive (_path: name: param: { "${name}" = param.option; }) ps; + +} diff --git a/nixos/modules/services/networking/strongswan-swanctl/swanctl-params.nix b/nixos/modules/services/networking/strongswan-swanctl/swanctl-params.nix new file mode 100644 index 000000000000..ad211f41eef0 --- /dev/null +++ b/nixos/modules/services/networking/strongswan-swanctl/swanctl-params.nix @@ -0,0 +1,1168 @@ +# See: https://wiki.strongswan.org/projects/strongswan/wiki/Swanctlconf +# +# When strongSwan is upgraded please update the parameters in this file. You can +# see which parameters should be deleted, changed or added by diffing +# swanctl.opt: +# +# git clone https://github.com/strongswan/strongswan.git +# cd strongswan +# git diff 5.5.3..5.6.0 src/swanctl/swanctl.opt + +lib: with (import ./param-constructors.nix lib); + +let + certParams = { + file = mkOptionalStrParam '' + Absolute path to the certificate to load. Passed as-is to the daemon, so + it must be readable by it. + </para><para> + Configure either this or <option>handle</option>, but not both, in one section. + ''; + + handle = mkOptionalHexParam '' + Hex-encoded CKA_ID or handle of the certificate on a token or TPM, + respectively. + </para><para> + Configure either this or <option>file</option>, but not both, in one section. + ''; + + slot = mkOptionalIntParam '' + Optional slot number of the token that stores the certificate. + ''; + + module = mkOptionalStrParam '' + Optional PKCS#11 module name. + ''; + }; +in { + authorities = mkAttrsOfParams ({ + + cacert = mkOptionalStrParam '' + The certificates may use a relative path from the swanctl + <literal>x509ca</literal> directory or an absolute path. + </para><para> + Configure one of <option>cacert</option>, + <option>file</option>, or + <option>handle</option> per section. + ''; + + cert_uri_base = mkOptionalStrParam '' + Defines the base URI for the Hash and URL feature supported by + IKEv2. Instead of exchanging complete certificates, IKEv2 allows one to + send an URI that resolves to the DER encoded certificate. The certificate + URIs are built by appending the SHA1 hash of the DER encoded certificates + to this base URI. + ''; + + crl_uris = mkCommaSepListParam [] '' + List of CRL distribution points (ldap, http, or file URI). + ''; + + ocsp_uris = mkCommaSepListParam [] '' + List of OCSP URIs. + ''; + + } // certParams) '' + Section defining complementary attributes of certification authorities, each + in its own subsection with an arbitrary yet unique name + ''; + + connections = mkAttrsOfParams { + + version = mkIntParam 0 '' + IKE major version to use for connection. + <itemizedlist> + <listitem><para>1 uses IKEv1 aka ISAKMP,</para></listitem> + <listitem><para>2 uses IKEv2.</para></listitem> + <listitem><para>A connection using the default of 0 accepts both IKEv1 and IKEv2 as + responder, and initiates the connection actively with IKEv2.</para></listitem> + </itemizedlist> + ''; + + local_addrs = mkCommaSepListParam [] '' + Local address(es) to use for IKE communication. Takes + single IPv4/IPv6 addresses, DNS names, CIDR subnets or IP address ranges. + </para><para> + As initiator, the first non-range/non-subnet is used to initiate the + connection from. As responder, the local destination address must match at + least to one of the specified addresses, subnets or ranges. + </para><para> + If FQDNs are assigned they are resolved every time a configuration lookup + is done. If DNS resolution times out, the lookup is delayed for that time. + ''; + + remote_addrs = mkCommaSepListParam [] '' + Remote address(es) to use for IKE communication. Takes + single IPv4/IPv6 addresses, DNS names, CIDR subnets or IP address ranges. + </para><para> + As initiator, the first non-range/non-subnet is used to initiate the + connection to. As responder, the initiator source address must match at + least to one of the specified addresses, subnets or ranges. + </para><para> + If FQDNs are assigned they are resolved every time a configuration lookup + is done. If DNS resolution times out, the lookup is delayed for that time. + To initiate a connection, at least one specific address or DNS name must + be specified. + ''; + + local_port = mkIntParam 500 '' + Local UDP port for IKE communication. By default the port of the socket + backend is used, which is usually <literal>500</literal>. If port + <literal>500</literal> is used, automatic IKE port floating to port + <literal>4500</literal> is used to work around NAT issues. + </para><para> + Using a non-default local IKE port requires support from the socket + backend in use (socket-dynamic). + ''; + + remote_port = mkIntParam 500 '' + Remote UDP port for IKE communication. If the default of port + <literal>500</literal> is used, automatic IKE port floating to port + <literal>4500</literal> is used to work around NAT issues. + ''; + + proposals = mkCommaSepListParam ["default"] '' + A proposal is a set of algorithms. For non-AEAD algorithms, this includes + for IKE an encryption algorithm, an integrity algorithm, a pseudo random + function and a Diffie-Hellman group. For AEAD algorithms, instead of + encryption and integrity algorithms, a combined algorithm is used. + </para><para> + In IKEv2, multiple algorithms of the same kind can be specified in a + single proposal, from which one gets selected. In IKEv1, only one + algorithm per kind is allowed per proposal, more algorithms get implicitly + stripped. Use multiple proposals to offer different algorithms + combinations in IKEv1. + </para><para> + Algorithm keywords get separated using dashes. Multiple proposals may be + specified in a list. The special value <literal>default</literal> forms a + default proposal of supported algorithms considered safe, and is usually a + good choice for interoperability. + ''; + + vips = mkCommaSepListParam [] '' + List of virtual IPs to request in IKEv2 configuration payloads or IKEv1 + Mode Config. The wildcard addresses <literal>0.0.0.0</literal> and + <literal>::</literal> request an arbitrary address, specific addresses may + be defined. The responder may return a different address, though, or none + at all. + ''; + + aggressive = mkYesNoParam no '' + Enables Aggressive Mode instead of Main Mode with Identity + Protection. Aggressive Mode is considered less secure, because the ID and + HASH payloads are exchanged unprotected. This allows a passive attacker to + snoop peer identities, and even worse, start dictionary attacks on the + Preshared Key. + ''; + + pull = mkYesNoParam yes '' + If the default of yes is used, Mode Config works in pull mode, where the + initiator actively requests a virtual IP. With no, push mode is used, + where the responder pushes down a virtual IP to the initiating peer. + </para><para> + Push mode is currently supported for IKEv1, but not in IKEv2. It is used + by a few implementations only, pull mode is recommended. + ''; + + dscp = mkStrParam "000000" '' + Differentiated Services Field Codepoint to set on outgoing IKE packets for + this connection. The value is a six digit binary encoded string specifying + the Codepoint to set, as defined in RFC 2474. + ''; + + encap = mkYesNoParam no '' + To enforce UDP encapsulation of ESP packets, the IKE daemon can fake the + NAT detection payloads. This makes the peer believe that NAT takes place + on the path, forcing it to encapsulate ESP packets in UDP. + </para><para> + Usually this is not required, but it can help to work around connectivity + issues with too restrictive intermediary firewalls. + ''; + + mobike = mkYesNoParam yes '' + Enables MOBIKE on IKEv2 connections. MOBIKE is enabled by default on IKEv2 + connections, and allows mobility of clients and multi-homing on servers by + migrating active IPsec tunnels. + </para><para> + Usually keeping MOBIKE enabled is unproblematic, as it is not used if the + peer does not indicate support for it. However, due to the design of + MOBIKE, IKEv2 always floats to port 4500 starting from the second + exchange. Some implementations don't like this behavior, hence it can be + disabled. + ''; + + dpd_delay = mkDurationParam "0s" '' + Interval to check the liveness of a peer actively using IKEv2 + INFORMATIONAL exchanges or IKEv1 R_U_THERE messages. Active DPD checking + is only enforced if no IKE or ESP/AH packet has been received for the + configured DPD delay. + ''; + + dpd_timeout = mkDurationParam "0s" '' + Charon by default uses the normal retransmission mechanism and timeouts to + check the liveness of a peer, as all messages are used for liveness + checking. For compatibility reasons, with IKEv1 a custom interval may be + specified; this option has no effect on connections using IKEv2. + ''; + + fragmentation = mkEnumParam ["yes" "accept" "force" "no"] "yes" '' + Use IKE fragmentation (proprietary IKEv1 extension or RFC 7383 IKEv2 + fragmentation). Acceptable values are <literal>yes</literal> (the default + since 5.5.1), <literal>accept</literal> (since versions:5.5.3), + <literal>force</literal> and <literal>no</literal>. + <itemizedlist> + <listitem><para>If set to <literal>yes</literal>, and the peer + supports it, oversized IKE messages will be sent in fragments.</para></listitem> + <listitem><para>If set to + <literal>accept</literal>, support for fragmentation is announced to the peer but the daemon + does not send its own messages in fragments.</para></listitem> + <listitem><para>If set to <literal>force</literal> (only + supported for IKEv1) the initial IKE message will already be fragmented if + required.</para></listitem> + <listitem><para>Finally, setting the option to <literal>no</literal> will disable announcing + support for this feature.</para></listitem> + </itemizedlist> + </para><para> + Note that fragmented IKE messages sent by a peer are always processed + irrespective of the value of this option (even when set to no). + ''; + + send_certreq = mkYesNoParam yes '' + Send certificate request payloads to offer trusted root CA certificates to + the peer. Certificate requests help the peer to choose an appropriate + certificate/private key for authentication and are enabled by default. + Disabling certificate requests can be useful if too many trusted root CA + certificates are installed, as each certificate request increases the size + of the initial IKE packets. + ''; + + send_cert = mkEnumParam ["always" "never" "ifasked" ] "ifasked" '' + Send certificate payloads when using certificate authentication. + <itemizedlist> + <listitem><para>With the default of <literal>ifasked</literal> the daemon sends + certificate payloads only if certificate requests have been received.</para></listitem> + <listitem><para><literal>never</literal> disables sending of certificate payloads + altogether,</para></listitem> + <listitem><para><literal>always</literal> causes certificate payloads to be sent + unconditionally whenever certificate authentication is used.</para></listitem> + </itemizedlist> + ''; + + keyingtries = mkIntParam 1 '' + Number of retransmission sequences to perform during initial + connect. Instead of giving up initiation after the first retransmission + sequence with the default value of <literal>1</literal>, additional + sequences may be started according to the configured value. A value of + <literal>0</literal> initiates a new sequence until the connection + establishes or fails with a permanent error. + ''; + + unique = mkEnumParam ["no" "never" "keep" "replace"] "no" '' + Connection uniqueness policy to enforce. To avoid multiple connections + from the same user, a uniqueness policy can be enforced. + </para><para> + <itemizedlist> + <listitem><para> + The value <literal>never</literal> does never enforce such a policy, even + if a peer included INITIAL_CONTACT notification messages, + </para></listitem> + <listitem><para> + whereas <literal>no</literal> replaces existing connections for the same + identity if a new one has the INITIAL_CONTACT notify. + </para></listitem> + <listitem><para> + <literal>keep</literal> rejects new connection attempts if the same user + already has an active connection, + </para></listitem> + <listitem><para> + <literal>replace</literal> deletes any existing connection if a new one + for the same user gets established. + </para></listitem> + </itemizedlist> + To compare connections for uniqueness, the remote IKE identity is used. If + EAP or XAuth authentication is involved, the EAP-Identity or XAuth + username is used to enforce the uniqueness policy instead. + </para><para> + On initiators this setting specifies whether an INITIAL_CONTACT notify is + sent during IKE_AUTH if no existing connection is found with the remote + peer (determined by the identities of the first authentication + round). Unless set to <literal>never</literal> the client will send a notify. + ''; + + reauth_time = mkDurationParam "0s" '' + Time to schedule IKE reauthentication. IKE reauthentication recreates the + IKE/ISAKMP SA from scratch and re-evaluates the credentials. In asymmetric + configurations (with EAP or configuration payloads) it might not be + possible to actively reauthenticate as responder. The IKEv2 + reauthentication lifetime negotiation can instruct the client to perform + reauthentication. + </para><para> + Reauthentication is disabled by default. Enabling it usually may lead to + small connection interruptions, as strongSwan uses a break-before-make + policy with IKEv2 to avoid any conflicts with associated tunnel resources. + ''; + + rekey_time = mkDurationParam "4h" '' + IKE rekeying refreshes key material using a Diffie-Hellman exchange, but + does not re-check associated credentials. It is supported in IKEv2 only, + IKEv1 performs a reauthentication procedure instead. + </para><para> + With the default value IKE rekeying is scheduled every 4 hours, minus the + configured rand_time. If a reauth_time is configured, rekey_time defaults + to zero, disabling rekeying; explicitly set both to enforce rekeying and + reauthentication. + ''; + + over_time = mkOptionalDurationParam '' + Hard IKE_SA lifetime if rekey/reauth does not complete, as time. To avoid + having an IKE/ISAKMP kept alive if IKE reauthentication or rekeying fails + perpetually, a maximum hard lifetime may be specified. If the IKE_SA fails + to rekey or reauthenticate within the specified time, the IKE_SA gets + closed. + </para><para> + In contrast to CHILD_SA rekeying, over_time is relative in time to the + rekey_time and reauth_time values, as it applies to both. + </para><para> + The default is 10% of the longer of <option>rekey_time</option> and + <option>reauth_time</option>. + ''; + + rand_time = mkOptionalDurationParam '' + Time range from which to choose a random value to subtract from + rekey/reauth times. To avoid having both peers initiating the rekey/reauth + procedure simultaneously, a random time gets subtracted from the + rekey/reauth times. + </para><para> + The default is equal to the configured <option>over_time</option>. + ''; + + pools = mkCommaSepListParam [] '' + List of named IP pools to allocate virtual IP addresses + and other configuration attributes from. Each name references a pool by + name from either the pools section or an external pool. + ''; + + mediation = mkYesNoParam no '' + Whether this connection is a mediation connection, that is, whether this + connection is used to mediate other connections using the IKEv2 Mediation + Extension. Mediation connections create no CHILD_SA. + ''; + + mediated_by = mkOptionalStrParam '' + The name of the connection to mediate this connection through. If given, + the connection will be mediated through the named mediation + connection. The mediation connection must have mediation enabled. + ''; + + mediation_peer = mkOptionalStrParam '' + Identity under which the peer is registered at the mediation server, that + is, the IKE identity the other end of this connection uses as its local + identity on its connection to the mediation server. This is the identity + we request the mediation server to mediate us with. Only relevant on + connections that set mediated_by. If it is not given, the remote IKE + identity of the first authentication round of this connection will be + used. + ''; + + local = mkPrefixedAttrsOfParams { + + round = mkIntParam 0 '' + Optional numeric identifier by which authentication rounds are + sorted. If not specified rounds are ordered by their position in the + config file/vici message. + ''; + + certs = mkCommaSepListParam [] '' + List of certificate candidates to use for + authentication. The certificates may use a relative path from the + swanctl <literal>x509</literal> directory or an absolute path. + </para><para> + The certificate used for authentication is selected based on the + received certificate request payloads. If no appropriate CA can be + located, the first certificate is used. + ''; + + cert = mkPostfixedAttrsOfParams certParams '' + Section for a certificate candidate to use for + authentication. Certificates in certs are transmitted as binary blobs, + these sections offer more flexibility. + ''; + + pubkeys = mkCommaSepListParam [] '' + List of raw public key candidates to use for + authentication. The public keys may use a relative path from the swanctl + <literal>pubkey</literal> directory or an absolute path. + </para><para> + Even though multiple local public keys could be defined in principle, + only the first public key in the list is used for authentication. + ''; + + auth = mkStrParam "pubkey" '' + Authentication to perform locally. + <itemizedlist> + <listitem><para> + The default <literal>pubkey</literal> uses public key authentication + using a private key associated to a usable certificate. + </para></listitem> + <listitem><para> + <literal>psk</literal> uses pre-shared key authentication. + </para></listitem> + <listitem><para> + The IKEv1 specific <literal>xauth</literal> is used for XAuth or Hybrid + authentication, + </para></listitem> + <listitem><para> + while the IKEv2 specific <literal>eap</literal> keyword defines EAP + authentication. + </para></listitem> + <listitem><para> + For <literal>xauth</literal>, a specific backend name may be appended, + separated by a dash. The appropriate <literal>xauth</literal> backend is + selected to perform the XAuth exchange. For traditional XAuth, the + <literal>xauth</literal> method is usually defined in the second + authentication round following an initial <literal>pubkey</literal> (or + <literal>psk</literal>) round. Using <literal>xauth</literal> in the + first round performs Hybrid Mode client authentication. + </para></listitem> + <listitem><para> + For <literal>eap</literal>, a specific EAP method name may be appended, separated by a + dash. An EAP module implementing the appropriate method is selected to + perform the EAP conversation. + </para></listitem> + <listitem><para> + Since 5.4.0, if both peers support RFC 7427 ("Signature Authentication + in IKEv2") specific hash algorithms to be used during IKEv2 + authentication may be configured. To do so use <literal>ike:</literal> + followed by a trust chain signature scheme constraint (see description + of the <option>remote</option> section's <option>auth</option> + keyword). For example, with <literal>ike:pubkey-sha384-sha256</literal> + a public key signature scheme with either SHA-384 or SHA-256 would get + used for authentication, in that order and depending on the hash + algorithms supported by the peer. If no specific hash algorithms are + configured, the default is to prefer an algorithm that matches or + exceeds the strength of the signature key. If no constraints with + <literal>ike:</literal> prefix are configured any signature scheme + constraint (without <literal>ike:</literal> prefix) will also apply to + IKEv2 authentication, unless this is disabled in + <literal>strongswan.conf</literal>. To use RSASSA-PSS signatures use + <literal>rsa/pss</literal> instead of <literal>pubkey</literal> or + <literal>rsa</literal> as in e.g. + <literal>ike:rsa/pss-sha256</literal>. If <literal>pubkey</literal> or + <literal>rsa</literal> constraints are configured RSASSA-PSS signatures + will only be used if enabled in <literal>strongswan.conf</literal>(5). + </para></listitem> + </itemizedlist> + ''; + + id = mkOptionalStrParam '' + IKE identity to use for authentication round. When using certificate + authentication, the IKE identity must be contained in the certificate, + either as subject or as subjectAltName. + ''; + + eap_id = mkOptionalStrParam '' + Client EAP-Identity to use in EAP-Identity exchange and the EAP method. + ''; + + aaa_id = mkOptionalStrParam '' + Server side EAP-Identity to expect in the EAP method. Some EAP methods, + such as EAP-TLS, use an identity for the server to perform mutual + authentication. This identity may differ from the IKE identity, + especially when EAP authentication is delegated from the IKE responder + to an AAA backend. + </para><para> + For EAP-(T)TLS, this defines the identity for which the server must + provide a certificate in the TLS exchange. + ''; + + xauth_id = mkOptionalStrParam '' + Client XAuth username used in the XAuth exchange. + ''; + + } '' + Section for a local authentication round. A local authentication round + defines the rules how authentication is performed for the local + peer. Multiple rounds may be defined to use IKEv2 RFC 4739 Multiple + Authentication or IKEv1 XAuth. + </para><para> + Each round is defined in a section having <literal>local</literal> as + prefix, and an optional unique suffix. To define a single authentication + round, the suffix may be omitted. + ''; + + remote = mkPrefixedAttrsOfParams { + + round = mkIntParam 0 '' + Optional numeric identifier by which authentication rounds are + sorted. If not specified rounds are ordered by their position in the + config file/vici message. + ''; + + id = mkStrParam "%any" '' + IKE identity to expect for authentication round. When using certificate + authentication, the IKE identity must be contained in the certificate, + either as subject or as subjectAltName. + ''; + + eap_id = mkOptionalStrParam '' + Identity to use as peer identity during EAP authentication. If set to + <literal>%any</literal> the EAP-Identity method will be used to ask the + client for an EAP identity. + ''; + + groups = mkCommaSepListParam [] '' + Authorization group memberships to require. The peer + must prove membership to at least one of the specified groups. Group + membership can be certified by different means, for example by + appropriate Attribute Certificates or by an AAA backend involved in the + authentication. + ''; + + cert_policy = mkCommaSepListParam [] '' + List of certificate policy OIDs the peer's certificate + must have. OIDs are specified using the numerical dotted representation. + ''; + + certs = mkCommaSepListParam [] '' + List of certificates to accept for authentication. The certificates may + use a relative path from the swanctl <literal>x509</literal> directory + or an absolute path. + ''; + + cert = mkPostfixedAttrsOfParams certParams '' + Section for a certificate candidate to use for + authentication. Certificates in certs are transmitted as binary blobs, + these sections offer more flexibility. + ''; + + cacerts = mkCommaSepListParam [] '' + List of CA certificates to accept for + authentication. The certificates may use a relative path from the + swanctl <literal>x509ca</literal> directory or an absolute path. + ''; + + cacert = mkPostfixedAttrsOfParams certParams '' + Section for a CA certificate to accept for authentication. Certificates + in cacerts are transmitted as binary blobs, these sections offer more + flexibility. + ''; + + pubkeys = mkCommaSepListParam [] '' + List of raw public keys to accept for + authentication. The public keys may use a relative path from the swanctl + <literal>pubkey</literal> directory or an absolute path. + ''; + + revocation = mkEnumParam ["strict" "ifuri" "relaxed"] "relaxed" '' + Certificate revocation policy for CRL or OCSP revocation. + <itemizedlist> + <listitem><para> + A <literal>strict</literal> revocation policy fails if no revocation information is + available, i.e. the certificate is not known to be unrevoked. + </para></listitem> + <listitem><para> + <literal>ifuri</literal> fails only if a CRL/OCSP URI is available, but certificate + revocation checking fails, i.e. there should be revocation information + available, but it could not be obtained. + </para></listitem> + <listitem><para> + The default revocation policy <literal>relaxed</literal> fails only if a certificate is + revoked, i.e. it is explicitly known that it is bad. + </para></listitem> + </itemizedlist> + ''; + + auth = mkStrParam "pubkey" '' + Authentication to expect from remote. See the <option>local</option> + section's <option>auth</option> keyword description about the details of + supported mechanisms. + </para><para> + Since 5.4.0, to require a trustchain public key strength for the remote + side, specify the key type followed by the minimum strength in bits (for + example <literal>ecdsa-384</literal> or + <literal>rsa-2048-ecdsa-256</literal>). To limit the acceptable set of + hashing algorithms for trustchain validation, append hash algorithms to + pubkey or a key strength definition (for example + <literal>pubkey-sha256-sha512</literal>, + <literal>rsa-2048-sha256-sha384-sha512</literal> or + <literal>rsa-2048-sha256-ecdsa-256-sha256-sha384</literal>). + Unless disabled in <literal>strongswan.conf</literal>, or explicit IKEv2 + signature constraints are configured (refer to the description of the + <option>local</option> section's <option>auth</option> keyword for + details), such key types and hash algorithms are also applied as + constraints against IKEv2 signature authentication schemes used by the + remote side. To require RSASSA-PSS signatures use + <literal>rsa/pss</literal> instead of <literal>pubkey</literal> or + <literal>rsa</literal> as in e.g. <literal>rsa/pss-sha256</literal>. If + <literal>pubkey</literal> or <literal>rsa</literal> constraints are + configured RSASSA-PSS signatures will only be accepted if enabled in + <literal>strongswan.conf</literal>(5). + </para><para> + To specify trust chain constraints for EAP-(T)TLS, append a colon to the + EAP method, followed by the key type/size and hash algorithm as + discussed above (e.g. <literal>eap-tls:ecdsa-384-sha384</literal>). + ''; + + } '' + Section for a remote authentication round. A remote authentication round + defines the constraints how the peers must authenticate to use this + connection. Multiple rounds may be defined to use IKEv2 RFC 4739 Multiple + Authentication or IKEv1 XAuth. + </para><para> + Each round is defined in a section having <literal>remote</literal> as + prefix, and an optional unique suffix. To define a single authentication + round, the suffix may be omitted. + ''; + + children = mkAttrsOfParams { + ah_proposals = mkCommaSepListParam [] '' + AH proposals to offer for the CHILD_SA. A proposal is a set of + algorithms. For AH, this includes an integrity algorithm and an optional + Diffie-Hellman group. If a DH group is specified, CHILD_SA/Quick Mode + rekeying and initial negotiation uses a separate Diffie-Hellman exchange + using the specified group (refer to esp_proposals for details). + </para><para> + In IKEv2, multiple algorithms of the same kind can be specified in a + single proposal, from which one gets selected. In IKEv1, only one + algorithm per kind is allowed per proposal, more algorithms get + implicitly stripped. Use multiple proposals to offer different algorithms + combinations in IKEv1. + </para><para> + Algorithm keywords get separated using dashes. Multiple proposals may be + specified in a list. The special value <literal>default</literal> forms + a default proposal of supported algorithms considered safe, and is + usually a good choice for interoperability. By default no AH proposals + are included, instead ESP is proposed. + ''; + + esp_proposals = mkCommaSepListParam ["default"] '' + ESP proposals to offer for the CHILD_SA. A proposal is a set of + algorithms. For ESP non-AEAD proposals, this includes an integrity + algorithm, an encryption algorithm, an optional Diffie-Hellman group and + an optional Extended Sequence Number Mode indicator. For AEAD proposals, + a combined mode algorithm is used instead of the separate + encryption/integrity algorithms. + </para><para> + If a DH group is specified, CHILD_SA/Quick Mode rekeying and initial + negotiation use a separate Diffie-Hellman exchange using the specified + group. However, for IKEv2, the keys of the CHILD_SA created implicitly + with the IKE_SA will always be derived from the IKE_SA's key material. So + any DH group specified here will only apply when the CHILD_SA is later + rekeyed or is created with a separate CREATE_CHILD_SA exchange. A + proposal mismatch might, therefore, not immediately be noticed when the + SA is established, but may later cause rekeying to fail. + </para><para> + Extended Sequence Number support may be indicated with the + <literal>esn</literal> and <literal>noesn</literal> values, both may be + included to indicate support for both modes. If omitted, + <literal>noesn</literal> is assumed. + </para><para> + In IKEv2, multiple algorithms of the same kind can be specified in a + single proposal, from which one gets selected. In IKEv1, only one + algorithm per kind is allowed per proposal, more algorithms get + implicitly stripped. Use multiple proposals to offer different algorithms + combinations in IKEv1. + </para><para> + Algorithm keywords get separated using dashes. Multiple proposals may be + specified as a list. The special value <literal>default</literal> forms + a default proposal of supported algorithms considered safe, and is + usually a good choice for interoperability. If no algorithms are + specified for AH nor ESP, the default set of algorithms for ESP is + included. + ''; + + sha256_96 = mkYesNoParam no '' + HMAC-SHA-256 is used with 128-bit truncation with IPsec. For + compatibility with implementations that incorrectly use 96-bit truncation + this option may be enabled to configure the shorter truncation length in + the kernel. This is not negotiated, so this only works with peers that + use the incorrect truncation length (or have this option enabled). + ''; + + local_ts = mkCommaSepListParam ["dynamic"] '' + List of local traffic selectors to include in CHILD_SA. Each selector is + a CIDR subnet definition, followed by an optional proto/port + selector. The special value <literal>dynamic</literal> may be used + instead of a subnet definition, which gets replaced by the tunnel outer + address or the virtual IP, if negotiated. This is the default. + </para><para> + A protocol/port selector is surrounded by opening and closing square + brackets. Between these brackets, a numeric or getservent(3) protocol + name may be specified. After the optional protocol restriction, an + optional port restriction may be specified, separated by a slash. The + port restriction may be numeric, a getservent(3) service name, or the + special value <literal>opaque</literal> for RFC 4301 OPAQUE + selectors. Port ranges may be specified as well, none of the kernel + backends currently support port ranges, though. + </para><para> + When IKEv1 is used only the first selector is interpreted, except if the + Cisco Unity extension plugin is used. This is due to a limitation of the + IKEv1 protocol, which only allows a single pair of selectors per + CHILD_SA. So to tunnel traffic matched by several pairs of selectors when + using IKEv1 several children (CHILD_SAs) have to be defined that cover + the selectors. The IKE daemon uses traffic selector narrowing for IKEv1, + the same way it is standardized and implemented for IKEv2. However, this + may lead to problems with other implementations. To avoid that, configure + identical selectors in such scenarios. + ''; + + remote_ts = mkCommaSepListParam ["dynamic"] '' + List of remote selectors to include in CHILD_SA. See + <option>local_ts</option> for a description of the selector syntax. + ''; + + rekey_time = mkDurationParam "1h" '' + Time to schedule CHILD_SA rekeying. CHILD_SA rekeying refreshes key + material, optionally using a Diffie-Hellman exchange if a group is + specified in the proposal. To avoid rekey collisions initiated by both + ends simultaneously, a value in the range of <option>rand_time</option> + gets subtracted to form the effective soft lifetime. + </para><para> + By default CHILD_SA rekeying is scheduled every hour, minus + <option>rand_time</option>. + ''; + + life_time = mkOptionalDurationParam '' + Maximum lifetime before CHILD_SA gets closed. Usually this hard lifetime + is never reached, because the CHILD_SA gets rekeyed before. If that fails + for whatever reason, this limit closes the CHILD_SA. The default is 10% + more than the <option>rekey_time</option>. + ''; + + rand_time = mkOptionalDurationParam '' + Time range from which to choose a random value to subtract from + <option>rekey_time</option>. The default is the difference between + <option>life_time</option> and <option>rekey_time</option>. + ''; + + rekey_bytes = mkIntParam 0 '' + Number of bytes processed before initiating CHILD_SA rekeying. CHILD_SA + rekeying refreshes key material, optionally using a Diffie-Hellman + exchange if a group is specified in the proposal. + </para><para> + To avoid rekey collisions initiated by both ends simultaneously, a value + in the range of <option>rand_bytes</option> gets subtracted to form the + effective soft volume limit. + </para><para> + Volume based CHILD_SA rekeying is disabled by default. + ''; + + life_bytes = mkOptionalIntParam '' + Maximum bytes processed before CHILD_SA gets closed. Usually this hard + volume limit is never reached, because the CHILD_SA gets rekeyed + before. If that fails for whatever reason, this limit closes the + CHILD_SA. The default is 10% more than <option>rekey_bytes</option>. + ''; + + rand_bytes = mkOptionalIntParam '' + Byte range from which to choose a random value to subtract from + <option>rekey_bytes</option>. The default is the difference between + <option>life_bytes</option> and <option>rekey_bytes</option>. + ''; + + rekey_packets = mkIntParam 0 '' + Number of packets processed before initiating CHILD_SA rekeying. CHILD_SA + rekeying refreshes key material, optionally using a Diffie-Hellman + exchange if a group is specified in the proposal. + </para><para> + To avoid rekey collisions initiated by both ends simultaneously, a value + in the range of <option>rand_packets</option> gets subtracted to form + the effective soft packet count limit. + </para><para> + Packet count based CHILD_SA rekeying is disabled by default. + ''; + + life_packets = mkOptionalIntParam '' + Maximum number of packets processed before CHILD_SA gets closed. Usually + this hard packets limit is never reached, because the CHILD_SA gets + rekeyed before. If that fails for whatever reason, this limit closes the + CHILD_SA. + </para><para> + The default is 10% more than <option>rekey_bytes</option>. + ''; + + rand_packets = mkOptionalIntParam '' + Packet range from which to choose a random value to subtract from + <option>rekey_packets</option>. The default is the difference between + <option>life_packets</option> and <option>rekey_packets</option>. + ''; + + updown = mkOptionalStrParam '' + Updown script to invoke on CHILD_SA up and down events. + ''; + + hostaccess = mkYesNoParam yes '' + Hostaccess variable to pass to <literal>updown</literal> script. + ''; + + mode = mkEnumParam [ "tunnel" + "transport" + "transport_proxy" + "beet" + "pass" + "drop" + ] "tunnel" '' + IPsec Mode to establish CHILD_SA with. + <itemizedlist> + <listitem><para> + <literal>tunnel</literal> negotiates the CHILD_SA in IPsec Tunnel Mode, + </para></listitem> + <listitem><para> + whereas <literal>transport</literal> uses IPsec Transport Mode. + </para></listitem> + <listitem><para> + <literal>transport_proxy</literal> signifying the special Mobile IPv6 + Transport Proxy Mode. + </para></listitem> + <listitem><para> + <literal>beet</literal> is the Bound End to End Tunnel mixture mode, + working with fixed inner addresses without the need to include them in + each packet. + </para></listitem> + <listitem><para> + Both <literal>transport</literal> and <literal>beet</literal> modes are + subject to mode negotiation; <literal>tunnel</literal> mode is + negotiated if the preferred mode is not available. + </para></listitem> + <listitem><para> + <literal>pass</literal> and <literal>drop</literal> are used to install + shunt policies which explicitly bypass the defined traffic from IPsec + processing or drop it, respectively. + </para></listitem> + </itemizedlist> + ''; + + policies = mkYesNoParam yes '' + Whether to install IPsec policies or not. Disabling this can be useful in + some scenarios e.g. MIPv6, where policies are not managed by the IKE + daemon. Since 5.3.3. + ''; + + policies_fwd_out = mkYesNoParam no '' + Whether to install outbound FWD IPsec policies or not. Enabling this is + required in case there is a drop policy that would match and block + forwarded traffic for this CHILD_SA. Since 5.5.1. + ''; + + dpd_action = mkEnumParam ["clear" "trap" "restart"] "clear" '' + Action to perform for this CHILD_SA on DPD timeout. The default clear + closes the CHILD_SA and does not take further action. trap installs a + trap policy, which will catch matching traffic and tries to re-negotiate + the tunnel on-demand. restart immediately tries to re-negotiate the + CHILD_SA under a fresh IKE_SA. + ''; + + ipcomp = mkYesNoParam no '' + Enable IPComp compression before encryption. If enabled, IKE tries to + negotiate IPComp compression to compress ESP payload data prior to + encryption. + ''; + + inactivity = mkDurationParam "0s" '' + Timeout before closing CHILD_SA after inactivity. If no traffic has been + processed in either direction for the configured timeout, the CHILD_SA + gets closed due to inactivity. The default value of 0 disables inactivity + checks. + ''; + + reqid = mkIntParam 0 '' + Fixed reqid to use for this CHILD_SA. This might be helpful in some + scenarios, but works only if each CHILD_SA configuration is instantiated + not more than once. The default of 0 uses dynamic reqids, allocated + incrementally. + ''; + + priority = mkIntParam 0 '' + Optional fixed priority for IPsec policies. This could be useful to + install high-priority drop policies. The default of 0 uses dynamically + calculated priorities based on the size of the traffic selectors. + ''; + + interface = mkOptionalStrParam '' + Optional interface name to restrict outbound IPsec policies. + ''; + + mark_in = mkStrParam "0/0x00000000" '' + Netfilter mark and mask for input traffic. On Linux, Netfilter may + require marks on each packet to match an SA/policy having that option + set. This allows installing duplicate policies and enables Netfilter + rules to select specific SAs/policies for incoming traffic. Note that + inbound marks are only set on policies, by default, unless + <option>mark_in_sa</option> is enabled. The special value + <literal>%unique</literal> sets a unique mark on each CHILD_SA instance, + beyond that the value <literal>%unique-dir</literal> assigns a different + unique mark for each + </para><para> + An additional mask may be appended to the mark, separated by + <literal>/</literal>. The default mask if omitted is + <literal>0xffffffff</literal>. + ''; + + mark_in_sa = mkYesNoParam no '' + Whether to set <option>mark_in</option> on the inbound SA. By default, + the inbound mark is only set on the inbound policy. The tuple destination + address, protocol and SPI is unique and the mark is not required to find + the correct SA, allowing to mark traffic after decryption instead (where + more specific selectors may be used) to match different policies. Marking + packets before decryption is still possible, even if no mark is set on + the SA. + ''; + + mark_out = mkStrParam "0/0x00000000" '' + Netfilter mark and mask for output traffic. On Linux, Netfilter may + require marks on each packet to match a policy/SA having that option + set. This allows installing duplicate policies and enables Netfilter + rules to select specific policies/SAs for outgoing traffic. The special + value <literal>%unique</literal> sets a unique mark on each CHILD_SA + instance, beyond that the value <literal>%unique-dir</literal> assigns a + different unique mark for each CHILD_SA direction (in/out). + </para><para> + An additional mask may be appended to the mark, separated by + <literal>/</literal>. The default mask if omitted is + <literal>0xffffffff</literal>. + ''; + + tfc_padding = mkParamOfType (with lib.types; either int (enum ["mtu"])) 0 '' + Pads ESP packets with additional data to have a consistent ESP packet + size for improved Traffic Flow Confidentiality. The padding defines the + minimum size of all ESP packets sent. The default value of + <literal>0</literal> disables TFC padding, the special value + <literal>mtu</literal> adds TFC padding to create a packet size equal to + the Path Maximum Transfer Unit. + ''; + + replay_window = mkIntParam 32 '' + IPsec replay window to configure for this CHILD_SA. Larger values than + the default of <literal>32</literal> are supported using the Netlink + backend only, a value of <literal>0</literal> disables IPsec replay + protection. + ''; + + hw_offload = mkYesNoParam no '' + Enable hardware offload for this CHILD_SA, if supported by the IPsec + implementation. + ''; + + start_action = mkEnumParam ["none" "trap" "start"] "none" '' + Action to perform after loading the configuration. + <itemizedlist> + <listitem><para> + The default of <literal>none</literal> loads the connection only, which + then can be manually initiated or used as a responder configuration. + </para></listitem> + <listitem><para> + The value <literal>trap</literal> installs a trap policy, which triggers + the tunnel as soon as matching traffic has been detected. + </para></listitem> + <listitem><para> + The value <literal>start</literal> initiates the connection actively. + </para></listitem> + </itemizedlist> + When unloading or replacing a CHILD_SA configuration having a + <option>start_action</option> different from <literal>none</literal>, + the inverse action is performed. Configurations with + <literal>start</literal> get closed, while such with + <literal>trap</literal> get uninstalled. + ''; + + close_action = mkEnumParam ["none" "trap" "start"] "none" '' + Action to perform after a CHILD_SA gets closed by the peer. + <itemizedlist> + <listitem><para> + The default of <literal>none</literal> does not take any action, + </para></listitem> + <listitem><para> + <literal>trap</literal> installs a trap policy for the CHILD_SA. + </para></listitem> + <listitem><para> + <literal>start</literal> tries to re-create the CHILD_SA. + </para></listitem> + </itemizedlist> + </para><para> + <option>close_action</option> does not provide any guarantee that the + CHILD_SA is kept alive. It acts on explicit close messages only, but not + on negotiation failures. Use trap policies to reliably re-create failed + CHILD_SAs. + ''; + + } '' + CHILD_SA configuration sub-section. Each connection definition may have + one or more sections in its <option>children</option> subsection. The + section name defines the name of the CHILD_SA configuration, which must be + unique within the connection (denoted <child> below). + ''; + } '' + Section defining IKE connection configurations, each in its own subsection + with an arbitrary yet unique name + ''; + + secrets = let + mkEapXauthParams = mkPrefixedAttrsOfParams { + secret = mkOptionalStrParam '' + Value of the EAP/XAuth secret. It may either be an ASCII string, a hex + encoded string if it has a 0x prefix or a Base64 encoded string if it + has a 0s prefix in its value. + ''; + + id = mkPrefixedAttrsOfParam (mkOptionalStrParam "") '' + Identity the EAP/XAuth secret belongs to. Multiple unique identities may + be specified, each having an <literal>id</literal> prefix, if a secret + is shared between multiple users. + ''; + + } '' + EAP secret section for a specific secret. Each EAP secret is defined in a + unique section having the <literal>eap</literal> prefix. EAP secrets are + used for XAuth authentication as well. + ''; + + in { + + eap = mkEapXauthParams; + xauth = mkEapXauthParams; + + ntlm = mkPrefixedAttrsOfParams { + secret = mkOptionalStrParam '' + Value of the NTLM secret, which is the NT Hash of the actual secret, + that is, MD4(UTF-16LE(secret)). The resulting 16-byte value may either + be given as a hex encoded string with a 0x prefix or as a Base64 encoded + string with a 0s prefix. + ''; + + id = mkPrefixedAttrsOfParam (mkOptionalStrParam "") '' + Identity the NTLM secret belongs to. Multiple unique identities may be + specified, each having an id prefix, if a secret is shared between + multiple users. + ''; + } '' + NTLM secret section for a specific secret. Each NTLM secret is defined in + a unique section having the <literal>ntlm</literal> prefix. NTLM secrets + may only be used for EAP-MSCHAPv2 authentication. + ''; + + ike = mkPrefixedAttrsOfParams { + secret = mkOptionalStrParam '' + Value of the IKE preshared secret. It may either be an ASCII string, a + hex encoded string if it has a 0x prefix or a Base64 encoded string if + it has a 0s prefix in its value. + ''; + + id = mkPrefixedAttrsOfParam (mkOptionalStrParam "") '' + IKE identity the IKE preshared secret belongs to. Multiple unique + identities may be specified, each having an <literal>id</literal> + prefix, if a secret is shared between multiple peers. + ''; + } '' + IKE preshared secret section for a specific secret. Each IKE PSK is + defined in a unique section having the <literal>ike</literal> prefix. + ''; + + private = mkPrefixedAttrsOfParams { + file = mkOptionalStrParam '' + File name in the private folder for which this passphrase should be used. + ''; + + secret = mkOptionalStrParam '' + Value of decryption passphrase for private key. + ''; + } '' + Private key decryption passphrase for a key in the + <literal>private</literal> folder. + ''; + + rsa = mkPrefixedAttrsOfParams { + file = mkOptionalStrParam '' + File name in the <literal>rsa</literal> folder for which this passphrase + should be used. + ''; + secret = mkOptionalStrParam '' + Value of decryption passphrase for RSA key. + ''; + } '' + Private key decryption passphrase for a key in the <literal>rsa</literal> + folder. + ''; + + ecdsa = mkPrefixedAttrsOfParams { + file = mkOptionalStrParam '' + File name in the <literal>ecdsa</literal> folder for which this + passphrase should be used. + ''; + secret = mkOptionalStrParam '' + Value of decryption passphrase for ECDSA key. + ''; + } '' + Private key decryption passphrase for a key in the + <literal>ecdsa</literal> folder. + ''; + + pkcs8 = mkPrefixedAttrsOfParams { + file = mkOptionalStrParam '' + File name in the <literal>pkcs8</literal> folder for which this + passphrase should be used. + ''; + secret = mkOptionalStrParam '' + Value of decryption passphrase for PKCS#8 key. + ''; + } '' + Private key decryption passphrase for a key in the + <literal>pkcs8</literal> folder. + ''; + + pkcs12 = mkPrefixedAttrsOfParams { + file = mkOptionalStrParam '' + File name in the <literal>pkcs12</literal> folder for which this + passphrase should be used. + ''; + secret = mkOptionalStrParam '' + Value of decryption passphrase for PKCS#12 container. + ''; + } '' + PKCS#12 decryption passphrase for a container in the + <literal>pkcs12</literal> folder. + ''; + + token = mkPrefixedAttrsOfParams { + handle = mkOptionalHexParam '' + Hex-encoded CKA_ID or handle of the private key on the token or TPM, + respectively. + ''; + + slot = mkOptionalIntParam '' + Optional slot number to access the token. + ''; + + module = mkOptionalStrParam '' + Optional PKCS#11 module name to access the token. + ''; + + pin = mkOptionalStrParam '' + Optional PIN required to access the key on the token. If none is + provided the user is prompted during an interactive + <literal>--load-creds</literal> call. + ''; + } ''Definition for a private key that's stored on a token/smartcard/TPM.''; + + }; + + pools = mkAttrsOfParams { + addrs = mkOptionalStrParam '' + Subnet or range defining addresses allocated in pool. Accepts a single + CIDR subnet defining the pool to allocate addresses from or an address + range (<from>-<to>). Pools must be unique and non-overlapping. + ''; + + dns = mkCommaSepListParam [] "Address or CIDR subnets"; + nbns = mkCommaSepListParam [] "Address or CIDR subnets"; + dhcp = mkCommaSepListParam [] "Address or CIDR subnets"; + netmask = mkCommaSepListParam [] "Address or CIDR subnets"; + server = mkCommaSepListParam [] "Address or CIDR subnets"; + subnet = mkCommaSepListParam [] "Address or CIDR subnets"; + split_include = mkCommaSepListParam [] "Address or CIDR subnets"; + split_exclude = mkCommaSepListParam [] "Address or CIDR subnets"; + } '' + Section defining named pools. Named pools may be referenced by connections + with the pools option to assign virtual IPs and other configuration + attributes. Each pool must have a unique name (denoted <name> below). + ''; +} diff --git a/nixos/modules/services/networking/tcpcrypt.nix b/nixos/modules/services/networking/tcpcrypt.nix index 2f304165eb4b..ee005e11aa32 100644 --- a/nixos/modules/services/networking/tcpcrypt.nix +++ b/nixos/modules/services/networking/tcpcrypt.nix @@ -44,9 +44,9 @@ in path = [ pkgs.iptables pkgs.tcpcrypt pkgs.procps ]; preStart = '' - mkdir -p /var/run/tcpcryptd - chown tcpcryptd /var/run/tcpcryptd - sysctl -n net.ipv4.tcp_ecn >/run/pre-tcpcrypt-ecn-state + mkdir -p /run/tcpcryptd + chown tcpcryptd /run/tcpcryptd + sysctl -n net.ipv4.tcp_ecn > /run/tcpcryptd/pre-tcpcrypt-ecn-state sysctl -w net.ipv4.tcp_ecn=0 iptables -t raw -N nixos-tcpcrypt @@ -61,8 +61,8 @@ in script = "tcpcryptd -x 0x10"; postStop = '' - if [ -f /run/pre-tcpcrypt-ecn-state ]; then - sysctl -w net.ipv4.tcp_ecn=$(cat /run/pre-tcpcrypt-ecn-state) + if [ -f /run/tcpcryptd/pre-tcpcrypt-ecn-state ]; then + sysctl -w net.ipv4.tcp_ecn=$(cat /run/tcpcryptd/pre-tcpcrypt-ecn-state) fi iptables -t mangle -D POSTROUTING -j nixos-tcpcrypt || true diff --git a/nixos/modules/services/networking/unbound.nix b/nixos/modules/services/networking/unbound.nix index 545ee327d596..f069a9883a7f 100644 --- a/nixos/modules/services/networking/unbound.nix +++ b/nixos/modules/services/networking/unbound.nix @@ -112,7 +112,7 @@ in mkdir -m 0755 -p ${stateDir}/dev/ cp ${confFile} ${stateDir}/unbound.conf ${optionalString cfg.enableRootTrustAnchor '' - ${pkgs.unbound}/bin/unbound-anchor -a ${rootTrustAnchorFile} + ${pkgs.unbound}/bin/unbound-anchor -a ${rootTrustAnchorFile} || echo "Root anchor updated!" chown unbound ${stateDir} ${rootTrustAnchorFile} ''} touch ${stateDir}/dev/random diff --git a/nixos/modules/services/networking/unifi.nix b/nixos/modules/services/networking/unifi.nix index 8e5f0bfc070d..94958bfdd83e 100644 --- a/nixos/modules/services/networking/unifi.nix +++ b/nixos/modules/services/networking/unifi.nix @@ -4,22 +4,22 @@ let cfg = config.services.unifi; stateDir = "/var/lib/unifi"; cmd = '' - @${pkgs.jre}/bin/java java \ + @${cfg.jrePackage}/bin/java java \ ${optionalString (cfg.initialJavaHeapSize != null) "-Xms${(toString cfg.initialJavaHeapSize)}m"} \ ${optionalString (cfg.maximumJavaHeapSize != null) "-Xmx${(toString cfg.maximumJavaHeapSize)}m"} \ -jar ${stateDir}/lib/ace.jar ''; mountPoints = [ { - what = "${pkgs.unifi}/dl"; + what = "${cfg.unifiPackage}/dl"; where = "${stateDir}/dl"; } { - what = "${pkgs.unifi}/lib"; + what = "${cfg.unifiPackage}/lib"; where = "${stateDir}/lib"; } { - what = "${pkgs.mongodb}/bin"; + what = "${cfg.mongodbPackage}/bin"; where = "${stateDir}/bin"; } { @@ -41,6 +41,33 @@ in ''; }; + services.unifi.jrePackage = mkOption { + type = types.package; + default = pkgs.jre8; + defaultText = "pkgs.jre8"; + description = '' + The JRE package to use. Check the release notes to ensure it is supported. + ''; + }; + + services.unifi.unifiPackage = mkOption { + type = types.package; + default = pkgs.unifiLTS; + defaultText = "pkgs.unifiLTS"; + description = '' + The unifi package to use. + ''; + }; + + services.unifi.mongodbPackage = mkOption { + type = types.package; + default = pkgs.mongodb; + defaultText = "pkgs.mongodb"; + description = '' + The mongodb package to use. + ''; + }; + services.unifi.dataDir = mkOption { type = types.str; default = "${stateDir}/data"; @@ -137,7 +164,7 @@ in rm -rf "${stateDir}/webapps" mkdir -p "${stateDir}/webapps" chown unifi "${stateDir}/webapps" - ln -s "${pkgs.unifi}/webapps/ROOT" "${stateDir}/webapps/ROOT" + ln -s "${cfg.unifiPackage}/webapps/ROOT" "${stateDir}/webapps/ROOT" ''; postStop = '' diff --git a/nixos/modules/services/networking/wireguard.nix b/nixos/modules/services/networking/wireguard.nix index 24accd41511c..0591917c7423 100644 --- a/nixos/modules/services/networking/wireguard.nix +++ b/nixos/modules/services/networking/wireguard.nix @@ -53,30 +53,30 @@ let }; preSetup = mkOption { - example = literalExample ['' + example = literalExample '' ${pkgs.iproute}/bin/ip netns add foo - '']; - default = []; - type = with types; listOf str; + ''; + default = ""; + type = with types; coercedTo (listOf str) (concatStringsSep "\n") lines; description = '' - A list of commands called at the start of the interface setup. + Commands called at the start of the interface setup. ''; }; postSetup = mkOption { - example = literalExample ['' - ${pkgs.bash} -c 'printf "nameserver 10.200.100.1" | ${pkgs.openresolv}/bin/resolvconf -a wg0 -m 0' - '']; - default = []; - type = with types; listOf str; - description = "A list of commands called at the end of the interface setup."; + example = literalExample '' + printf "nameserver 10.200.100.1" | ${pkgs.openresolv}/bin/resolvconf -a wg0 -m 0 + ''; + default = ""; + type = with types; coercedTo (listOf str) (concatStringsSep "\n") lines; + description = "Commands called at the end of the interface setup."; }; postShutdown = mkOption { - example = literalExample ["${pkgs.openresolv}/bin/resolvconf -d wg0"]; - default = []; - type = with types; listOf str; - description = "A list of commands called after shutting down the interface."; + example = literalExample "${pkgs.openresolv}/bin/resolvconf -d wg0"; + default = ""; + type = with types; coercedTo (listOf str) (concatStringsSep "\n") lines; + description = "Commands called after shutting down the interface."; }; table = mkOption { @@ -182,9 +182,6 @@ let }; - ipCommand = "${pkgs.iproute}/bin/ip"; - wgCommand = "${pkgs.wireguard}/bin/wg"; - generateUnit = name: values: # exactly one way to specify the private key must be set assert (values.privateKey != null) != (values.privateKeyFile != null); @@ -196,49 +193,53 @@ let after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; environment.DEVICE = name; + path = with pkgs; [ kmod iproute wireguard ]; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; - ExecStart = flatten([ - values.preSetup + }; + + script = '' + modprobe wireguard + + ${values.preSetup} - "-${ipCommand} link del dev ${name}" - "${ipCommand} link add dev ${name} type wireguard" + ip link add dev ${name} type wireguard - (map (ip: - "${ipCommand} address add ${ip} dev ${name}" - ) values.ips) + ${concatMapStringsSep "\n" (ip: + "ip address add ${ip} dev ${name}" + ) values.ips} - ("${wgCommand} set ${name} private-key ${privKey}" + - optionalString (values.listenPort != null) " listen-port ${toString values.listenPort}") + wg set ${name} private-key ${privKey} ${ + optionalString (values.listenPort != null) " listen-port ${toString values.listenPort}"} - (map (peer: + ${concatMapStringsSep "\n" (peer: assert (peer.presharedKeyFile == null) || (peer.presharedKey == null); # at most one of the two must be set let psk = if peer.presharedKey != null then pkgs.writeText "wg-psk" peer.presharedKey else peer.presharedKeyFile; in - "${wgCommand} set ${name} peer ${peer.publicKey}" + - optionalString (psk != null) " preshared-key ${psk}" + - optionalString (peer.endpoint != null) " endpoint ${peer.endpoint}" + - optionalString (peer.persistentKeepalive != null) " persistent-keepalive ${toString peer.persistentKeepalive}" + - optionalString (peer.allowedIPs != []) " allowed-ips ${concatStringsSep "," peer.allowedIPs}" - ) values.peers) - - "${ipCommand} link set up dev ${name}" - - (optionals (values.allowedIPsAsRoutes != false) (map (peer: - (map (allowedIP: - "${ipCommand} route replace ${allowedIP} dev ${name} table ${values.table}" - ) peer.allowedIPs) - ) values.peers)) - - values.postSetup - ]); - ExecStop = flatten([ - "${ipCommand} link del dev ${name}" - values.postShutdown - ]); - }; + "wg set ${name} peer ${peer.publicKey}" + + optionalString (psk != null) " preshared-key ${psk}" + + optionalString (peer.endpoint != null) " endpoint ${peer.endpoint}" + + optionalString (peer.persistentKeepalive != null) " persistent-keepalive ${toString peer.persistentKeepalive}" + + optionalString (peer.allowedIPs != []) " allowed-ips ${concatStringsSep "," peer.allowedIPs}" + ) values.peers} + + ip link set up dev ${name} + + ${optionalString (values.allowedIPsAsRoutes != false) (concatStringsSep "\n" (concatMap (peer: + (map (allowedIP: + "ip route replace ${allowedIP} dev ${name} table ${values.table}" + ) peer.allowedIPs) + ) values.peers))} + + ${values.postSetup} + ''; + + preStop = '' + ip link del dev ${name} + ${values.postShutdown} + ''; }; in diff --git a/nixos/modules/services/networking/zerotierone.nix b/nixos/modules/services/networking/zerotierone.nix index 86e0204ec2f7..cd1617b8e2ba 100644 --- a/nixos/modules/services/networking/zerotierone.nix +++ b/nixos/modules/services/networking/zerotierone.nix @@ -7,6 +7,16 @@ let in { options.services.zerotierone.enable = mkEnableOption "ZeroTierOne"; + + options.services.zerotierone.joinNetworks = mkOption { + default = []; + example = [ "a8a2c3c10c1a68de" ]; + type = types.listOf types.str; + description = '' + List of ZeroTier Network IDs to join on startup + ''; + }; + options.services.zerotierone.package = mkOption { default = pkgs.zerotierone; defaultText = "pkgs.zerotierone"; @@ -22,12 +32,13 @@ in path = [ cfg.package ]; after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; - preStart = - '' - mkdir -p /var/lib/zerotier-one + preStart = '' + mkdir -p /var/lib/zerotier-one/networks.d chmod 700 /var/lib/zerotier-one chown -R root:root /var/lib/zerotier-one - ''; + '' + (concatMapStrings (netId: '' + touch "/var/lib/zerotier-one/networks.d/${netId}.conf" + '') cfg.joinNetworks); serviceConfig = { ExecStart = "${cfg.package}/bin/zerotier-one"; Restart = "always"; @@ -38,6 +49,9 @@ in # ZeroTier does not issue DHCP leases, but some strangers might... networking.dhcpcd.denyInterfaces = [ "zt0" ]; + # ZeroTier receives UDP transmissions on port 9993 by default + networking.firewall.allowedUDPPorts = [ 9993 ]; + environment.systemPackages = [ cfg.package ]; }; } diff --git a/nixos/modules/services/printing/cupsd.nix b/nixos/modules/services/printing/cupsd.nix index 4c7f58d1d8bc..c4147986439c 100644 --- a/nixos/modules/services/printing/cupsd.nix +++ b/nixos/modules/services/printing/cupsd.nix @@ -83,6 +83,8 @@ let WebInterface ${if cfg.webInterface then "Yes" else "No"} + LogLevel ${cfg.logLevel} + ${cfg.extraConf} ''; @@ -124,7 +126,7 @@ in listenAddresses = mkOption { type = types.listOf types.str; - default = [ "127.0.0.1:631" ]; + default = [ "localhost:631" ]; example = [ "*:631" ]; description = '' A list of addresses and ports on which to listen. @@ -165,6 +167,15 @@ in ''; }; + logLevel = mkOption { + type = types.str; + default = "info"; + example = "debug"; + description = '' + Specifies the cupsd logging verbosity. + ''; + }; + extraFilesConf = mkOption { type = types.lines; default = ""; @@ -180,7 +191,7 @@ in example = '' BrowsePoll cups.example.com - LogLevel debug + MaxCopies 42 ''; description = '' Extra contents of the configuration file of the CUPS daemon @@ -321,7 +332,10 @@ in ''} ''; - serviceConfig.PrivateTmp = true; + serviceConfig = { + PrivateTmp = true; + RuntimeDirectory = [ "cups" ]; + }; }; systemd.services.cups-browsed = mkIf avahiEnabled @@ -342,8 +356,6 @@ in services.printing.extraConf = '' - LogLevel info - DefaultAuthType Basic <Location /> diff --git a/nixos/modules/services/search/elasticsearch.nix b/nixos/modules/services/search/elasticsearch.nix index adef500b7b5c..d61f588205af 100644 --- a/nixos/modules/services/search/elasticsearch.nix +++ b/nixos/modules/services/search/elasticsearch.nix @@ -32,8 +32,11 @@ let (if es5 then (pkgs.writeTextDir "log4j2.properties" cfg.logging) else (pkgs.writeTextDir "logging.yml" cfg.logging)) ]; - # Elasticsearch 5.x won't start when the scripts directory does not exist - postBuild = if es5 then "${pkgs.coreutils}/bin/mkdir -p $out/scripts" else ""; + postBuild = concatStringsSep "\n" (concatLists [ + # Elasticsearch 5.x won't start when the scripts directory does not exist + (optional es5 "${pkgs.coreutils}/bin/mkdir -p $out/scripts") + (optional es6 "ln -s ${cfg.package}/config/jvm.options $out/jvm.options") + ]); }; esPlugins = pkgs.buildEnv { diff --git a/nixos/modules/services/security/hologram-server.nix b/nixos/modules/services/security/hologram-server.nix index e267fed27955..bad02c7440ba 100644 --- a/nixos/modules/services/security/hologram-server.nix +++ b/nixos/modules/services/security/hologram-server.nix @@ -12,16 +12,20 @@ let dn = cfg.ldapBindDN; password = cfg.ldapBindPassword; }; - insecureldap = cfg.ldapInsecure; - userattr = cfg.ldapUserAttr; - baseDN = cfg.ldapBaseDN; + insecureldap = cfg.ldapInsecure; + userattr = cfg.ldapUserAttr; + baseDN = cfg.ldapBaseDN; + enableldapRoles = cfg.enableLdapRoles; + roleAttr = cfg.roleAttr; + groupClassAttr = cfg.groupClassAttr; }; aws = { account = cfg.awsAccount; defaultrole = cfg.awsDefaultRole; }; - stats = cfg.statsAddress; - listen = cfg.listenAddress; + stats = cfg.statsAddress; + listen = cfg.listenAddress; + cachetimeout = cfg.cacheTimeoutSeconds; }); in { options = { @@ -70,6 +74,24 @@ in { description = "Password of account to use to query the LDAP server"; }; + enableLdapRoles = mkOption { + type = types.bool; + default = false; + description = "Whether to assign user roles based on the user's LDAP group memberships"; + }; + + groupClassAttr = mkOption { + type = types.str; + default = "groupOfNames"; + description = "The objectclass attribute to search for groups when enableLdapRoles is true"; + }; + + roleAttr = mkOption { + type = types.str; + default = "businessCategory"; + description = "Which LDAP group attribute to search for authorized role ARNs"; + }; + awsAccount = mkOption { type = types.str; description = "AWS account number"; @@ -85,6 +107,12 @@ in { default = ""; description = "Address of statsd server"; }; + + cacheTimeoutSeconds = mkOption { + type = types.int; + default = 3600; + description = "How often (in seconds) to refresh the LDAP cache"; + }; }; }; diff --git a/nixos/modules/services/security/oauth2_proxy.nix b/nixos/modules/services/security/oauth2_proxy.nix index ef48d52e7a94..433d97c2a7d7 100644 --- a/nixos/modules/services/security/oauth2_proxy.nix +++ b/nixos/modules/services/security/oauth2_proxy.nix @@ -6,70 +6,81 @@ with lib; let cfg = config.services.oauth2_proxy; - # Use like: - # repeatedArgs (arg: "--arg=${arg}") args - repeatedArgs = concatMapStringsSep " "; - # oauth2_proxy provides many options that are only relevant if you are using # a certain provider. This set maps from provider name to a function that # takes the configuration and returns a string that can be inserted into the # command-line to launch oauth2_proxy. providerSpecificOptions = { - azure = cfg: '' - --azure-tenant=${cfg.azure.tenant} \ - --resource=${cfg.azure.resource} \ - ''; - - github = cfg: '' - ${optionalString (!isNull cfg.github.org) "--github-org=${cfg.github.org}"} \ - ${optionalString (!isNull cfg.github.team) "--github-org=${cfg.github.team}"} \ - ''; - - google = cfg: '' - --google-admin-email=${cfg.google.adminEmail} \ - --google-service-account=${cfg.google.serviceAccountJSON} \ - ${repeatedArgs (group: "--google-group=${group}") cfg.google.groups} \ - ''; + azure = cfg: { + azure.tenant = cfg.azure.tenant; + resource = cfg.azure.resource; + }; + + github = cfg: { github = { + inherit (cfg.github) org team; + }; }; + + google = cfg: { google = with cfg.google; optionalAttrs (groups != []) { + admin-email = adminEmail; + service-account = serviceAccountJSON; + group = groups; + }; }; }; authenticatedEmailsFile = pkgs.writeText "authenticated-emails" cfg.email.addresses; - getProviderOptions = cfg: provider: providerSpecificOptions.${provider} or (_: "") cfg; - - mkCommandLine = cfg: '' - --provider='${cfg.provider}' \ - ${optionalString (!isNull cfg.email.addresses) "--authenticated-emails-file='${authenticatedEmailsFile}'"} \ - --approval-prompt='${cfg.approvalPrompt}' \ - ${optionalString (cfg.passBasicAuth && !isNull cfg.basicAuthPassword) "--basic-auth-password='${cfg.basicAuthPassword}'"} \ - --client-id='${cfg.clientID}' \ - --client-secret='${cfg.clientSecret}' \ - ${optionalString (!isNull cfg.cookie.domain) "--cookie-domain='${cfg.cookie.domain}'"} \ - --cookie-expire='${cfg.cookie.expire}' \ - --cookie-httponly=${boolToString cfg.cookie.httpOnly} \ - --cookie-name='${cfg.cookie.name}' \ - --cookie-secret='${cfg.cookie.secret}' \ - --cookie-secure=${boolToString cfg.cookie.secure} \ - ${optionalString (!isNull cfg.cookie.refresh) "--cookie-refresh='${cfg.cookie.refresh}'"} \ - ${optionalString (!isNull cfg.customTemplatesDir) "--custom-templates-dir='${cfg.customTemplatesDir}'"} \ - ${repeatedArgs (x: "--email-domain='${x}'") cfg.email.domains} \ - --http-address='${cfg.httpAddress}' \ - ${optionalString (!isNull cfg.htpasswd.file) "--htpasswd-file='${cfg.htpasswd.file}' --display-htpasswd-form=${boolToString cfg.htpasswd.displayForm}"} \ - ${optionalString (!isNull cfg.loginURL) "--login-url='${cfg.loginURL}'"} \ - --pass-access-token=${boolToString cfg.passAccessToken} \ - --pass-basic-auth=${boolToString cfg.passBasicAuth} \ - --pass-host-header=${boolToString cfg.passHostHeader} \ - --proxy-prefix='${cfg.proxyPrefix}' \ - ${optionalString (!isNull cfg.profileURL) "--profile-url='${cfg.profileURL}'"} \ - ${optionalString (!isNull cfg.redeemURL) "--redeem-url='${cfg.redeemURL}'"} \ - ${optionalString (!isNull cfg.redirectURL) "--redirect-url='${cfg.redirectURL}'"} \ - --request-logging=${boolToString cfg.requestLogging} \ - ${optionalString (!isNull cfg.scope) "--scope='${cfg.scope}'"} \ - ${repeatedArgs (x: "--skip-auth-regex='${x}'") cfg.skipAuthRegexes} \ - ${optionalString (!isNull cfg.signatureKey) "--signature-key='${cfg.signatureKey}'"} \ - --upstream='${cfg.upstream}' \ - ${optionalString (!isNull cfg.validateURL) "--validate-url='${cfg.validateURL}'"} \ - ${optionalString cfg.tls.enable "--tls-cert='${cfg.tls.certificate}' --tls-key='${cfg.tls.key}' --https-address='${cfg.tls.httpsAddress}'"} \ - '' + getProviderOptions cfg cfg.provider; + getProviderOptions = cfg: provider: providerSpecificOptions.${provider} or (_: {}) cfg; + + allConfig = with cfg; { + inherit (cfg) provider scope upstream; + approval-prompt = approvalPrompt; + basic-auth-password = basicAuthPassword; + client-id = clientID; + client-secret = clientSecret; + custom-templates-dir = customTemplatesDir; + email-domain = email.domains; + http-address = httpAddress; + login-url = loginURL; + pass-access-token = passAccessToken; + pass-basic-auth = passBasicAuth; + pass-host-header = passHostHeader; + proxy-prefix = proxyPrefix; + profile-url = profileURL; + redeem-url = redeemURL; + redirect-url = redirectURL; + request-logging = requestLogging; + skip-auth-regex = skipAuthRegexes; + signature-key = signatureKey; + validate-url = validateURL; + htpasswd-file = htpasswd.file; + cookie = { + inherit (cookie) domain secure expire name secret refresh; + httponly = cookie.httpOnly; + }; + set-xauthrequest = setXauthrequest; + } // lib.optionalAttrs (!isNull cfg.email.addresses) { + authenticated-emails-file = authenticatedEmailsFile; + } // lib.optionalAttrs (cfg.passBasicAuth) { + basic-auth-password = cfg.basicAuthPassword; + } // lib.optionalAttrs (!isNull cfg.htpasswd.file) { + display-htpasswd-file = cfg.htpasswd.displayForm; + } // lib.optionalAttrs tls.enable { + tls-cert = tls.certificate; + tls-key = tls.key; + https-address = tls.httpsAddress; + } // (getProviderOptions cfg cfg.provider) // cfg.extraConfig; + + mapConfig = key: attr: + if (!isNull attr && attr != []) then ( + if (builtins.typeOf attr) == "set" then concatStringsSep " " + (mapAttrsToList (name: value: mapConfig (key + "-" + name) value) attr) else + if (builtins.typeOf attr) == "list" then concatMapStringsSep " " (mapConfig key) attr else + if (builtins.typeOf attr) == "bool" then "--${key}=${boolToString attr}" else + if (builtins.typeOf attr) == "string" then "--${key}='${attr}'" else + "--${key}=${toString attr}") + else ""; + + configString = concatStringsSep " " (mapAttrsToList mapConfig allConfig); in { options.services.oauth2_proxy = { @@ -110,7 +121,7 @@ in }; clientID = mkOption { - type = types.str; + type = types.nullOr types.str; description = '' The OAuth Client ID. ''; @@ -118,7 +129,7 @@ in }; clientSecret = mkOption { - type = types.str; + type = types.nullOr types.str; description = '' The OAuth Client Secret. ''; @@ -272,7 +283,8 @@ in #################################################### # UPSTREAM Configuration upstream = mkOption { - type = types.commas; + type = with types; coercedTo string (x: [x]) (listOf string); + default = []; description = '' The http url(s) of the upstream endpoint or <literal>file://</literal> paths for static files. Routing is based on the path. @@ -365,7 +377,7 @@ in }; secret = mkOption { - type = types.str; + type = types.nullOr types.str; description = '' The seed string for secure cookies. ''; @@ -494,10 +506,43 @@ in ''; }; + setXauthrequest = mkOption { + type = types.nullOr types.bool; + default = false; + description = '' + Set X-Auth-Request-User and X-Auth-Request-Email response headers (useful in Nginx auth_request mode). Setting this to 'null' means using the upstream default (false). + ''; + }; + + extraConfig = mkOption { + default = {}; + description = '' + Extra config to pass to oauth2_proxy. + ''; + }; + + keyFile = mkOption { + type = types.nullOr types.string; + default = null; + description = '' + oauth2_proxy allows passing sensitive configuration via environment variables. + Make a file that contains lines like + OAUTH2_PROXY_CLIENT_SECRET=asdfasdfasdf.apps.googleuserscontent.com + and specify the path here. + ''; + example = "/run/keys/oauth2_proxy"; + }; + }; config = mkIf cfg.enable { + services.oauth2_proxy = mkIf (!isNull cfg.keyFile) { + clientID = mkDefault null; + clientSecret = mkDefault null; + cookie.secret = mkDefault null; + }; + users.extraUsers.oauth2_proxy = { description = "OAuth2 Proxy"; }; @@ -511,7 +556,8 @@ in serviceConfig = { User = "oauth2_proxy"; Restart = "always"; - ExecStart = "${cfg.package.bin}/bin/oauth2_proxy ${mkCommandLine cfg}"; + ExecStart = "${cfg.package.bin}/bin/oauth2_proxy ${configString}"; + EnvironmentFile = mkIf (cfg.keyFile != null) cfg.keyFile; }; }; diff --git a/nixos/modules/services/security/tor.nix b/nixos/modules/services/security/tor.nix index fed91756e769..806252f49b8d 100644 --- a/nixos/modules/services/security/tor.nix +++ b/nixos/modules/services/security/tor.nix @@ -5,6 +5,7 @@ with lib; let cfg = config.services.tor; torDirectory = "/var/lib/tor"; + torRunDirectory = "/run/tor"; opt = name: value: optionalString (value != null) "${name} ${value}"; optint = name: value: optionalString (value != null && value != 0) "${name} ${toString value}"; @@ -38,6 +39,7 @@ let ''} ${optint "ControlPort" cfg.controlPort} + ${optionalString cfg.controlSocket.enable "ControlSocket ${torRunDirectory}/control GroupWritable RelaxDirModeCheck"} '' # Client connection config + optionalString cfg.client.enable '' @@ -140,6 +142,17 @@ in ''; }; + controlSocket = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Wheter to enable Tor control socket. Control socket is created + in <literal>${torRunDirectory}/control</literal> + ''; + }; + }; + client = { enable = mkOption { type = types.bool; @@ -690,14 +703,10 @@ in after = [ "network.target" ]; restartTriggers = [ torRcFile ]; - # Translated from the upstream contrib/dist/tor.service.in - preStart = '' - install -o tor -g tor -d ${torDirectory}/onion - ${pkgs.tor}/bin/tor -f ${torRcFile} --verify-config - ''; - serviceConfig = { Type = "simple"; + # Translated from the upstream contrib/dist/tor.service.in + ExecStartPre = "${pkgs.tor}/bin/tor -f ${torRcFile} --verify-config"; ExecStart = "${pkgs.tor}/bin/tor -f ${torRcFile} --RunAsDaemon 0"; ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; KillSignal = "SIGINT"; @@ -712,11 +721,13 @@ in # DeviceAllow /dev/urandom r # .. but we can't specify DeviceAllow multiple times. 'closed' # is close enough. + RuntimeDirectory = "tor"; + StateDirectory = [ "tor" "tor/onion" ]; PrivateTmp = "yes"; DevicePolicy = "closed"; InaccessibleDirectories = "/home"; ReadOnlyDirectories = "/"; - ReadWriteDirectories = torDirectory; + ReadWriteDirectories = [torDirectory torRunDirectory]; NoNewPrivileges = "yes"; }; }; diff --git a/nixos/modules/services/security/torify.nix b/nixos/modules/services/security/torify.nix index a29cb3f33dae..08da726437ea 100644 --- a/nixos/modules/services/security/torify.nix +++ b/nixos/modules/services/security/torify.nix @@ -7,7 +7,7 @@ let torify = pkgs.writeTextFile { name = "tsocks"; text = '' - #!${pkgs.stdenv.shell} + #!${pkgs.runtimeShell} TSOCKS_CONF_FILE=${pkgs.writeText "tsocks.conf" cfg.tsocks.config} LD_PRELOAD="${pkgs.tsocks}/lib/libtsocks.so $LD_PRELOAD" "$@" ''; executable = true; diff --git a/nixos/modules/services/security/torsocks.nix b/nixos/modules/services/security/torsocks.nix index 1b5a05b21e77..c60c745443bc 100644 --- a/nixos/modules/services/security/torsocks.nix +++ b/nixos/modules/services/security/torsocks.nix @@ -23,7 +23,7 @@ let wrapTorsocks = name: server: pkgs.writeTextFile { name = name; text = '' - #!${pkgs.stdenv.shell} + #!${pkgs.runtimeShell} TORSOCKS_CONF_FILE=${pkgs.writeText "torsocks.conf" (configFile server)} ${pkgs.torsocks}/bin/torsocks "$@" ''; executable = true; diff --git a/nixos/modules/services/torrent/deluge.nix b/nixos/modules/services/torrent/deluge.nix index ec1e97f4125e..bff22cd13594 100644 --- a/nixos/modules/services/torrent/deluge.nix +++ b/nixos/modules/services/torrent/deluge.nix @@ -11,10 +11,7 @@ in { options = { services = { deluge = { - enable = mkOption { - default = false; - description = "Start the Deluge daemon"; - }; + enable = mkEnableOption "Deluge daemon"; openFilesLimit = mkOption { default = openFilesLimit; @@ -25,14 +22,7 @@ in { }; }; - deluge.web = { - enable = mkOption { - default = false; - description = '' - Start Deluge Web daemon. - ''; - }; - }; + deluge.web.enable = mkEnableOption "Deluge Web daemon"; }; }; diff --git a/nixos/modules/services/torrent/transmission.nix b/nixos/modules/services/torrent/transmission.nix index dd6b585b7e23..3564afd77f41 100644 --- a/nixos/modules/services/torrent/transmission.nix +++ b/nixos/modules/services/torrent/transmission.nix @@ -21,6 +21,19 @@ let # for users in group "transmission" to have access to torrents fullSettings = { umask = 2; download-dir = downloadDir; incomplete-dir = incompleteDir; } // cfg.settings; + + # Directories transmission expects to exist and be ug+rwx. + directoriesToManage = [ homeDir settingsDir fullSettings.download-dir fullSettings.incomplete-dir ]; + + preStart = pkgs.writeScript "transmission-pre-start" '' + #!${pkgs.runtimeShell} + set -ex + for DIR in ${escapeShellArgs directoriesToManage}; do + mkdir -p "$DIR" + chmod 770 "$DIR" + done + cp -f ${settingsFile} ${settingsDir}/settings.json + ''; in { options = { @@ -59,8 +72,8 @@ in time the service starts). String values must be quoted, integer and boolean values must not. - See https://trac.transmissionbt.com/wiki/EditConfigFiles for - documentation. + See https://github.com/transmission/transmission/wiki/Editing-Configuration-Files + for documentation. ''; }; @@ -89,9 +102,7 @@ in # 1) Only the "transmission" user and group have access to torrents. # 2) Optionally update/force specific fields into the configuration file. - serviceConfig.ExecStartPre = '' - ${pkgs.stdenv.shell} -c "mkdir -p ${homeDir} ${settingsDir} ${fullSettings.download-dir} ${fullSettings.incomplete-dir} && chmod 770 ${homeDir} ${settingsDir} ${fullSettings.download-dir} ${fullSettings.incomplete-dir} && rm -f ${settingsDir}/settings.json && cp -f ${settingsFile} ${settingsDir}/settings.json" - ''; + serviceConfig.ExecStartPre = preStart; serviceConfig.ExecStart = "${pkgs.transmission}/bin/transmission-daemon -f --port ${toString config.services.transmission.port}"; serviceConfig.ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; serviceConfig.User = "transmission"; @@ -136,6 +147,7 @@ in ${getLib pkgs.libcap}/lib/libcap*.so* mr, ${getLib pkgs.attr}/lib/libattr*.so* mr, ${getLib pkgs.lz4}/lib/liblz4*.so* mr, + ${getLib pkgs.libkrb5}/lib/lib*.so* mr, @{PROC}/sys/kernel/random/uuid r, @{PROC}/sys/vm/overcommit_memory r, diff --git a/nixos/modules/services/web-apps/atlassian/jira.nix b/nixos/modules/services/web-apps/atlassian/jira.nix index 81ee8154326c..13c5951524d9 100644 --- a/nixos/modules/services/web-apps/atlassian/jira.nix +++ b/nixos/modules/services/web-apps/atlassian/jira.nix @@ -155,7 +155,7 @@ in requires = [ "postgresql.service" ]; after = [ "postgresql.service" ]; - path = [ cfg.jrePackage ]; + path = [ cfg.jrePackage pkgs.bash ]; environment = { JIRA_USER = cfg.user; diff --git a/nixos/modules/services/web-apps/nixbot.nix b/nixos/modules/services/web-apps/nixbot.nix deleted file mode 100644 index 0592d01bf369..000000000000 --- a/nixos/modules/services/web-apps/nixbot.nix +++ /dev/null @@ -1,149 +0,0 @@ -{ config, lib, pkgs, ... }: - -with lib; - -let - cfg = config.services.nixbot; - pyramidIni = '' - ### - # app configuration - # http://docs.pylonsproject.org/projects/pyramid/en/1.7-branch/narr/environment.html - ### - - [app:main] - use = egg:nixbot - - nixbot.github_token = ${cfg.githubToken} - nixbot.bot_name = ${cfg.botName} - nixbot.repo = ${cfg.repo} - nixbot.pr_repo = ${cfg.prRepo} - nixbot.hydra_jobsets_repo = ${cfg.hydraJobsetsRepo} - nixbot.github_secret = justnotsorandom - nixbot.public_url = ${cfg.publicUrl} - nixbot.repo_dir = ${cfg.repoDir} - - pyramid.reload_templates = false - pyramid.debug_authorization = false - pyramid.debug_notfound = false - pyramid.debug_routematch = false - pyramid.default_locale_name = en - - # By default, the toolbar only appears for clients from IP addresses - # '127.0.0.1' and '::1'. - # debugtoolbar.hosts = 127.0.0.1 ::1 - - ### - # wsgi server configuration - ### - - [server:main] - use = egg:waitress#main - host = 0.0.0.0 - port = 6543 - - ### - # logging configuration - # http://docs.pylonsproject.org/projects/pyramid/en/1.7-branch/narr/logging.html - ### - - [loggers] - keys = root, nixbot - - [handlers] - keys = console - - [formatters] - keys = generic - - [logger_root] - level = INFO - handlers = console - - [logger_nixbot] - level = INFO - handlers = - qualname = nixbot - - [handler_console] - class = StreamHandler - args = (sys.stderr,) - level = NOTSET - formatter = generic - - [formatter_generic] - format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s - ''; -in { - options = { - services.nixbot = { - enable = mkEnableOption "nixbot"; - - botName = mkOption { - type = types.str; - description = "The bot's github user account name."; - default = "nixbot"; - }; - - githubToken = mkOption { - type = types.str; - description = "The bot's github user account token."; - example = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; - }; - - repo = mkOption { - type = types.str; - description = "The github repository to check for PRs."; - example = "nixos/nixpkgs"; - }; - - prRepo = mkOption { - type = types.str; - description = "The github repository to push the testing branches to."; - example = "nixos/nixpkgs-pr"; - }; - - hydraJobsetsRepo = mkOption { - type = types.str; - description = "The github repository to push the hydra jobset definitions to."; - example = "nixos/hydra-jobsets"; - }; - - publicUrl = mkOption { - type = types.str; - description = "The public URL the bot is reachable at (Github hook endpoint)."; - example = "https://nixbot.nixos.org"; - }; - - repoDir = mkOption { - type = types.path; - description = "The directory the repositories are stored in."; - default = "/var/lib/nixbot"; - }; - }; - }; - - config = mkIf cfg.enable { - users.extraUsers.nixbot = { - createHome = true; - home = cfg.repoDir; - }; - - systemd.services.nixbot = let - env = pkgs.python3.buildEnv.override { - extraLibs = [ pkgs.nixbot ]; - }; - in { - after = [ "network.target" ]; - wantedBy = [ "multi-user.target" ]; - script = '' - ${env}/bin/pserve ${pkgs.writeText "production.ini" pyramidIni} - ''; - - serviceConfig = { - User = "nixbot"; - Group = "nogroup"; - PermissionsStartOnly = true; - }; - }; - }; -} diff --git a/nixos/modules/services/web-apps/tt-rss.nix b/nixos/modules/services/web-apps/tt-rss.nix index 8f7a56189a07..610c6463a5eb 100644 --- a/nixos/modules/services/web-apps/tt-rss.nix +++ b/nixos/modules/services/web-apps/tt-rss.nix @@ -466,10 +466,10 @@ let ''; }; - services.nginx = { + # NOTE: No configuration is done if not using virtual host + services.nginx = mkIf (cfg.virtualHost != null) { enable = true; - # NOTE: No configuration is done if not using virtual host - virtualHosts = mkIf (cfg.virtualHost != null) { + virtualHosts = { "${cfg.virtualHost}" = { root = "${cfg.root}"; diff --git a/nixos/modules/services/web-apps/youtrack.nix b/nixos/modules/services/web-apps/youtrack.nix new file mode 100644 index 000000000000..e057e3025629 --- /dev/null +++ b/nixos/modules/services/web-apps/youtrack.nix @@ -0,0 +1,177 @@ +{ config, lib, pkgs, options, ... }: + +with lib; + +let + cfg = config.services.youtrack; + + extraAttr = concatStringsSep " " (mapAttrsToList (k: v: "-D${k}=${v}") (stdParams // cfg.extraParams)); + mergeAttrList = lib.foldl' lib.mergeAttrs {}; + + stdParams = mergeAttrList [ + (optionalAttrs (cfg.baseUrl != null) { + "jetbrains.youtrack.baseUrl" = cfg.baseUrl; + }) + { + "java.aws.headless" = "true"; + "jetbrains.youtrack.disableBrowser" = "true"; + } + ]; +in +{ + options.services.youtrack = { + + enable = mkEnableOption "YouTrack service"; + + address = mkOption { + description = '' + The interface youtrack will listen on. + ''; + default = "127.0.0.1"; + type = types.string; + }; + + baseUrl = mkOption { + description = '' + Base URL for youtrack. Will be auto-detected and stored in database. + ''; + type = types.nullOr types.string; + default = null; + }; + + extraParams = mkOption { + default = {}; + description = '' + Extra parameters to pass to youtrack. See + https://www.jetbrains.com/help/youtrack/standalone/YouTrack-Java-Start-Parameters.html + for more information. + ''; + example = { + "jetbrains.youtrack.overrideRootPassword" = "tortuga"; + }; + type = types.attrsOf types.string; + }; + + package = mkOption { + description = '' + Package to use. + ''; + type = types.package; + default = pkgs.youtrack; + defaultText = "pkgs.youtrack"; + }; + + port = mkOption { + description = '' + The port youtrack will listen on. + ''; + default = 8080; + type = types.int; + }; + + statePath = mkOption { + description = '' + Where to keep the youtrack database. + ''; + type = types.string; + default = "/var/lib/youtrack"; + }; + + virtualHost = mkOption { + description = '' + Name of the nginx virtual host to use and setup. + If null, do not setup anything. + ''; + default = null; + type = types.nullOr types.string; + }; + + jvmOpts = mkOption { + description = '' + Extra options to pass to the JVM. + See https://www.jetbrains.com/help/youtrack/standalone/Configure-JVM-Options.html + for more information. + ''; + type = types.string; + example = "-XX:MetaspaceSize=250m"; + default = ""; + }; + + maxMemory = mkOption { + description = '' + Maximum Java heap size + ''; + type = types.string; + default = "1g"; + }; + + maxMetaspaceSize = mkOption { + description = '' + Maximum java Metaspace memory. + ''; + type = types.string; + default = "350m"; + }; + }; + + config = mkIf cfg.enable { + + systemd.services.youtrack = { + environment.HOME = cfg.statePath; + environment.YOUTRACK_JVM_OPTS = "-Xmx${cfg.maxMemory} -XX:MaxMetaspaceSize=${cfg.maxMetaspaceSize} ${cfg.jvmOpts} ${extraAttr}"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "simple"; + User = "youtrack"; + Group = "youtrack"; + ExecStart = ''${cfg.package}/bin/youtrack ${cfg.address}:${toString cfg.port}''; + }; + }; + + users.users.youtrack = { + description = "Youtrack service user"; + isSystemUser = true; + home = cfg.statePath; + createHome = true; + group = "youtrack"; + }; + + users.groups.youtrack = {}; + + services.nginx = mkIf (cfg.virtualHost != null) { + upstreams.youtrack.servers."${cfg.address}:${toString cfg.port}" = {}; + virtualHosts.${cfg.virtualHost}.locations = { + "/" = { + proxyPass = "http://youtrack"; + extraConfig = '' + client_max_body_size 10m; + proxy_http_version 1.1; + proxy_set_header X-Forwarded-Host $http_host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + ''; + }; + + "/api/eventSourceBus" = { + proxyPass = "http://youtrack"; + extraConfig = '' + proxy_cache off; + proxy_buffering off; + proxy_read_timeout 86400s; + proxy_send_timeout 86400s; + proxy_set_header Connection ""; + chunked_transfer_encoding off; + client_max_body_size 10m; + proxy_http_version 1.1; + proxy_set_header X-Forwarded-Host $http_host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + ''; + }; + + }; + }; + + }; +} diff --git a/nixos/modules/services/web-servers/apache-httpd/owncloud.nix b/nixos/modules/services/web-servers/apache-httpd/owncloud.nix index cfddab2f5047..82b8bf3e30db 100644 --- a/nixos/modules/services/web-servers/apache-httpd/owncloud.nix +++ b/nixos/modules/services/web-servers/apache-httpd/owncloud.nix @@ -346,7 +346,7 @@ let postgresql = serverInfo.fullConfig.services.postgresql.package; setupDb = pkgs.writeScript "setup-owncloud-db" '' - #!${pkgs.stdenv.shell} + #!${pkgs.runtimeShell} PATH="${postgresql}/bin" createuser --no-superuser --no-createdb --no-createrole "${config.dbUser}" || true createdb "${config.dbName}" -O "${config.dbUser}" || true diff --git a/nixos/modules/services/web-servers/apache-httpd/per-server-options.nix b/nixos/modules/services/web-servers/apache-httpd/per-server-options.nix index 1d53ce659005..4bbd041b6e04 100644 --- a/nixos/modules/services/web-servers/apache-httpd/per-server-options.nix +++ b/nixos/modules/services/web-servers/apache-httpd/per-server-options.nix @@ -118,7 +118,7 @@ with lib; default = []; example = [ { urlPath = "/foo/bar.png"; - files = "/home/eelco/some-file.png"; + file = "/home/eelco/some-file.png"; } ]; description = '' diff --git a/nixos/modules/services/web-servers/caddy.nix b/nixos/modules/services/web-servers/caddy.nix index d8efa24bc6d5..2124a42f01a1 100644 --- a/nixos/modules/services/web-servers/caddy.nix +++ b/nixos/modules/services/web-servers/caddy.nix @@ -25,8 +25,8 @@ in { }; ca = mkOption { - default = "https://acme-v01.api.letsencrypt.org/directory"; - example = "https://acme-staging.api.letsencrypt.org/directory"; + default = "https://acme-v02.api.letsencrypt.org/directory"; + example = "https://acme-staging-v02.api.letsencrypt.org/directory"; type = types.string; description = "Certificate authority ACME server. The default (Let's Encrypt production server) should be fine for most people."; }; diff --git a/nixos/modules/services/web-servers/lighttpd/gitweb.nix b/nixos/modules/services/web-servers/lighttpd/gitweb.nix index c8d9836b0b68..c494d6966a7f 100644 --- a/nixos/modules/services/web-servers/lighttpd/gitweb.nix +++ b/nixos/modules/services/web-servers/lighttpd/gitweb.nix @@ -3,12 +3,10 @@ with lib; let - cfg = config.services.lighttpd.gitweb; - gitwebConfigFile = pkgs.writeText "gitweb.conf" '' - # path to git projects (<project>.git) - $projectroot = "${cfg.projectroot}"; - ${cfg.extraConfig} - ''; + cfg = config.services.gitweb; + package = pkgs.gitweb.override (optionalAttrs cfg.gitwebTheme { + gitwebTheme = true; + }); in { @@ -23,26 +21,9 @@ in ''; }; - projectroot = mkOption { - default = "/srv/git"; - type = types.path; - description = '' - Path to git projects (bare repositories) that should be served by - gitweb. Must not end with a slash. - ''; - }; - - extraConfig = mkOption { - default = ""; - type = types.lines; - description = '' - Verbatim configuration text appended to the generated gitweb.conf file. - ''; - }; - }; - config = mkIf cfg.enable { + config = mkIf config.services.lighttpd.gitweb.enable { # declare module dependencies services.lighttpd.enableModules = [ "mod_cgi" "mod_redirect" "mod_alias" "mod_setenv" ]; @@ -56,11 +37,11 @@ in "^/gitweb$" => "/gitweb/" ) alias.url = ( - "/gitweb/static/" => "${pkgs.git}/share/gitweb/static/", - "/gitweb/" => "${pkgs.git}/share/gitweb/gitweb.cgi" + "/gitweb/static/" => "${package}/static/", + "/gitweb/" => "${package}/gitweb.cgi" ) setenv.add-environment = ( - "GITWEB_CONFIG" => "${gitwebConfigFile}", + "GITWEB_CONFIG" => "${cfg.gitwebConfigFile}", "HOME" => "${cfg.projectroot}" ) } diff --git a/nixos/modules/services/web-servers/nginx/default.nix b/nixos/modules/services/web-servers/nginx/default.nix index dee877f1c114..815c3147e647 100644 --- a/nixos/modules/services/web-servers/nginx/default.nix +++ b/nixos/modules/services/web-servers/nginx/default.nix @@ -9,15 +9,16 @@ let serverName = if vhostConfig.serverName != null then vhostConfig.serverName else vhostName; + acmeDirectory = config.security.acme.directory; in vhostConfig // { inherit serverName; } // (optionalAttrs vhostConfig.enableACME { - sslCertificate = "/var/lib/acme/${serverName}/fullchain.pem"; - sslCertificateKey = "/var/lib/acme/${serverName}/key.pem"; + sslCertificate = "${acmeDirectory}/${serverName}/fullchain.pem"; + sslCertificateKey = "${acmeDirectory}/${serverName}/key.pem"; }) // (optionalAttrs (vhostConfig.useACMEHost != null) { - sslCertificate = "/var/lib/acme/${vhostConfig.useACMEHost}/fullchain.pem"; - sslCertificateKey = "/var/lib/acme/${vhostConfig.useACMEHost}/key.pem"; + sslCertificate = "${acmeDirectory}/${vhostConfig.useACMEHost}/fullchain.pem"; + sslCertificateKey = "${acmeDirectory}/${vhostConfig.useACMEHost}/key.pem"; }) ) cfg.virtualHosts; enableIPv6 = config.networking.enableIPv6; @@ -217,7 +218,10 @@ let ssl_certificate_key ${vhost.sslCertificateKey}; ''} - ${optionalString (vhost.basicAuth != {}) (mkBasicAuth vhostName vhost.basicAuth)} + ${optionalString (vhost.basicAuthFile != null || vhost.basicAuth != {}) '' + auth_basic secured; + auth_basic_user_file ${if vhost.basicAuthFile != null then vhost.basicAuthFile else mkHtpasswd vhostName vhost.basicAuth}; + ''} ${mkLocations vhost.locations} @@ -247,16 +251,11 @@ let ${optionalString (config.proxyPass != null && cfg.recommendedProxySettings) "include ${recommendedProxyConfig};"} } '') 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} + '') authDef) + ); in { diff --git a/nixos/modules/services/web-servers/nginx/gitweb.nix b/nixos/modules/services/web-servers/nginx/gitweb.nix new file mode 100644 index 000000000000..272fd1480185 --- /dev/null +++ b/nixos/modules/services/web-servers/nginx/gitweb.nix @@ -0,0 +1,61 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.gitweb; + package = pkgs.gitweb.override (optionalAttrs cfg.gitwebTheme { + gitwebTheme = true; + }); + +in +{ + + options.services.nginx.gitweb = { + + enable = mkOption { + default = false; + type = types.bool; + description = '' + If true, enable gitweb in nginx. Access it at http://yourserver/gitweb + ''; + }; + + }; + + config = mkIf config.services.nginx.gitweb.enable { + + systemd.services.gitweb = { + description = "GitWeb service"; + script = "${package}/gitweb.cgi --fastcgi --nproc=1"; + environment = { + FCGI_SOCKET_PATH = "/run/gitweb/gitweb.sock"; + }; + serviceConfig = { + User = "nginx"; + Group = "nginx"; + RuntimeDirectory = [ "gitweb" ]; + }; + wantedBy = [ "multi-user.target" ]; + }; + + services.nginx = { + virtualHosts.default = { + locations."/gitweb/static/" = { + alias = "${package}/static/"; + }; + locations."/gitweb/" = { + extraConfig = '' + include ${pkgs.nginx}/conf/fastcgi_params; + fastcgi_param GITWEB_CONFIG ${cfg.gitwebConfigFile}; + fastcgi_pass unix:/run/gitweb/gitweb.sock; + ''; + }; + }; + }; + + }; + + meta.maintainers = with maintainers; [ gnidorah ]; + +} diff --git a/nixos/modules/services/web-servers/nginx/vhost-options.nix b/nixos/modules/services/web-servers/nginx/vhost-options.nix index bf18108a1a3c..f014d817e80e 100644 --- a/nixos/modules/services/web-servers/nginx/vhost-options.nix +++ b/nixos/modules/services/web-servers/nginx/vhost-options.nix @@ -193,6 +193,14 @@ with lib; ''; }; + basicAuthFile = mkOption { + type = types.nullOr types.path; + default = null; + description = '' + Basic Auth password file for a vhost. + ''; + }; + locations = mkOption { type = types.attrsOf (types.submodule (import ./location-options.nix { inherit lib; diff --git a/nixos/modules/services/web-servers/varnish/default.nix b/nixos/modules/services/web-servers/varnish/default.nix index d63fb954ef96..bc74d62b116a 100644 --- a/nixos/modules/services/web-servers/varnish/default.nix +++ b/nixos/modules/services/web-servers/varnish/default.nix @@ -6,13 +6,22 @@ let cfg = config.services.varnish; commandLine = "-f ${pkgs.writeText "default.vcl" cfg.config}" + - optionalString (cfg.extraModules != []) " -p vmod_path='${makeSearchPathOutput "lib" "lib/varnish/vmods" ([pkgs.varnish] ++ cfg.extraModules)}' -r vmod_path"; + optionalString (cfg.extraModules != []) " -p vmod_path='${makeSearchPathOutput "lib" "lib/varnish/vmods" ([cfg.package] ++ cfg.extraModules)}' -r vmod_path"; in { options = { services.varnish = { enable = mkEnableOption "Varnish Server"; + package = mkOption { + type = types.package; + default = pkgs.varnish5; + defaultText = "pkgs.varnish5"; + description = '' + The package to use + ''; + }; + http_address = mkOption { type = types.str; default = "*:6081"; @@ -39,7 +48,7 @@ in extraModules = mkOption { type = types.listOf types.package; default = []; - example = literalExample "[ pkgs.varnish-geoip ]"; + example = literalExample "[ pkgs.varnish5Packages.geoip ]"; description = " Varnish modules (except 'std'). "; @@ -73,7 +82,7 @@ in serviceConfig = { Type = "simple"; PermissionsStartOnly = true; - ExecStart = "${pkgs.varnish}/sbin/varnishd -a ${cfg.http_address} -n ${cfg.stateDir} -F ${cfg.extraCommandLine} ${commandLine}"; + ExecStart = "${cfg.package}/sbin/varnishd -a ${cfg.http_address} -n ${cfg.stateDir} -F ${cfg.extraCommandLine} ${commandLine}"; Restart = "always"; RestartSec = "5s"; User = "varnish"; @@ -84,13 +93,13 @@ in }; }; - environment.systemPackages = [ pkgs.varnish ]; + environment.systemPackages = [ cfg.package ]; # check .vcl syntax at compile time (e.g. before nixops deployment) system.extraDependencies = [ (pkgs.stdenv.mkDerivation { name = "check-varnish-syntax"; - buildCommand = "${pkgs.varnish}/sbin/varnishd -C ${commandLine} 2> $out"; + buildCommand = "${cfg.package}/sbin/varnishd -C ${commandLine} 2> $out || (cat $out; exit 1)"; }) ]; diff --git a/nixos/modules/services/x11/desktop-managers/default.nix b/nixos/modules/services/x11/desktop-managers/default.nix index 4622c7b760f0..f435e85f6b83 100644 --- a/nixos/modules/services/x11/desktop-managers/default.nix +++ b/nixos/modules/services/x11/desktop-managers/default.nix @@ -87,11 +87,11 @@ in default = mkOption { type = types.str; - default = "none"; - example = "plasma5"; + default = ""; + example = "none"; description = "Default desktop manager loaded if none have been chosen."; apply = defaultDM: - if defaultDM == "none" && cfg.session.list != [] then + if defaultDM == "" && cfg.session.list != [] then (head cfg.session.list).name else if any (w: w.name == defaultDM) cfg.session.list then defaultDM diff --git a/nixos/modules/services/x11/desktop-managers/gnome3.nix b/nixos/modules/services/x11/desktop-managers/gnome3.nix index 7256013d5d8b..10e8ef0ed381 100644 --- a/nixos/modules/services/x11/desktop-managers/gnome3.nix +++ b/nixos/modules/services/x11/desktop-managers/gnome3.nix @@ -182,8 +182,7 @@ in { { inherit (pkgs) networkmanager modemmanager wpa_supplicant; inherit (pkgs.gnome3) networkmanager-openvpn networkmanager-vpnc networkmanager-openconnect networkmanager-fortisslvpn - networkmanager-pptp networkmanager-iodine - networkmanager-l2tp; }; + networkmanager-iodine networkmanager-l2tp; }; # Needed for themes and backgrounds environment.pathsToLink = [ "/share" ]; diff --git a/nixos/modules/services/x11/desktop-managers/lxqt.nix b/nixos/modules/services/x11/desktop-managers/lxqt.nix index fb907618d35b..2596ec4ad85c 100644 --- a/nixos/modules/services/x11/desktop-managers/lxqt.nix +++ b/nixos/modules/services/x11/desktop-managers/lxqt.nix @@ -61,6 +61,8 @@ in environment.variables.GIO_EXTRA_MODULES = [ "${pkgs.gvfs}/lib/gio/modules" ]; + services.upower.enable = config.powerManagement.enable; }; + } diff --git a/nixos/modules/services/x11/desktop-managers/mate.nix b/nixos/modules/services/x11/desktop-managers/mate.nix index 0117dc9d132b..db83aaf3c19f 100644 --- a/nixos/modules/services/x11/desktop-managers/mate.nix +++ b/nixos/modules/services/x11/desktop-managers/mate.nix @@ -108,6 +108,8 @@ in services.gnome3.gnome-keyring.enable = true; services.upower.enable = config.powerManagement.enable; + security.pam.services."mate-screensaver".unixAuth = true; + environment.pathsToLink = [ "/share" ]; }; diff --git a/nixos/modules/services/x11/desktop-managers/xfce.nix b/nixos/modules/services/x11/desktop-managers/xfce.nix index 489bffbee917..7dcc600d2664 100644 --- a/nixos/modules/services/x11/desktop-managers/xfce.nix +++ b/nixos/modules/services/x11/desktop-managers/xfce.nix @@ -128,7 +128,7 @@ in # Set GTK_DATA_PREFIX so that GTK+ can find the Xfce themes. export GTK_DATA_PREFIX=${config.system.path} - ${pkgs.stdenv.shell} ${pkgs.xfce.xinitrc} & + ${pkgs.runtimeShell} ${pkgs.xfce.xinitrc} & waitPID=$! ''; }]; diff --git a/nixos/modules/services/x11/display-managers/slim.nix b/nixos/modules/services/x11/display-managers/slim.nix index 0c4dd1973b53..f645a5c2f078 100644 --- a/nixos/modules/services/x11/display-managers/slim.nix +++ b/nixos/modules/services/x11/display-managers/slim.nix @@ -14,7 +14,7 @@ let default_xserver ${dmcfg.xserverBin} xserver_arguments ${toString dmcfg.xserverArgs} sessiondir ${dmcfg.session.desktops} - login_cmd exec ${pkgs.stdenv.shell} ${dmcfg.session.script} "%session" + login_cmd exec ${pkgs.runtimeShell} ${dmcfg.session.script} "%session" halt_cmd ${config.systemd.package}/sbin/shutdown -h now reboot_cmd ${config.systemd.package}/sbin/shutdown -r now logfile /dev/stderr diff --git a/nixos/modules/services/x11/window-managers/bspwm.nix b/nixos/modules/services/x11/window-managers/bspwm.nix index 6783ac3479e6..23cd4f6529a6 100644 --- a/nixos/modules/services/x11/window-managers/bspwm.nix +++ b/nixos/modules/services/x11/window-managers/bspwm.nix @@ -59,7 +59,7 @@ in start = '' export _JAVA_AWT_WM_NONREPARENTING=1 SXHKD_SHELL=/bin/sh ${cfg.sxhkd.package}/bin/sxhkd ${optionalString (cfg.sxhkd.configFile != null) "-c \"${cfg.sxhkd.configFile}\""} & - ${cfg.package}/bin/bspwm ${optionalString (cfg.configFile != null) "-c \"${cfg.configFile}\""} + ${cfg.package}/bin/bspwm ${optionalString (cfg.configFile != null) "-c \"${cfg.configFile}\""} & waitPID=$! ''; }; diff --git a/nixos/modules/services/x11/window-managers/default.nix b/nixos/modules/services/x11/window-managers/default.nix index 25ba95fccd75..e617e55a7a57 100644 --- a/nixos/modules/services/x11/window-managers/default.nix +++ b/nixos/modules/services/x11/window-managers/default.nix @@ -12,6 +12,7 @@ in ./afterstep.nix ./bspwm.nix ./dwm.nix + ./evilwm.nix ./exwm.nix ./fluxbox.nix ./fvwm.nix @@ -61,9 +62,7 @@ in example = "wmii"; description = "Default window manager loaded if none have been chosen."; apply = defaultWM: - if defaultWM == "none" && cfg.session != [] then - (head cfg.session).name - else if any (w: w.name == defaultWM) cfg.session then + if any (w: w.name == defaultWM) cfg.session then defaultWM else throw "Default window manager (${defaultWM}) not found."; diff --git a/nixos/modules/services/x11/xserver.nix b/nixos/modules/services/x11/xserver.nix index f96d3c5afbac..1404231f837e 100644 --- a/nixos/modules/services/x11/xserver.nix +++ b/nixos/modules/services/x11/xserver.nix @@ -161,15 +161,6 @@ in ''; }; - plainX = mkOption { - type = types.bool; - default = false; - description = '' - Whether the X11 session can be plain (without DM/WM) and - the Xsession script will be used as fallback or not. - ''; - }; - autorun = mkOption { type = types.bool; default = true; @@ -249,7 +240,10 @@ in type = types.listOf types.str; # !!! We'd like "nv" here, but it segfaults the X server. default = [ "ati" "cirrus" "intel" "vesa" "vmware" "modesetting" ]; - example = [ "vesa" ]; + example = [ + "ati_unfree" "amdgpu" "amdgpu-pro" + "nv" "nvidia" "nvidiaLegacy340" "nvidiaLegacy304" + ]; description = '' The names of the video drivers the configuration supports. They will be tried in order until one that @@ -561,11 +555,6 @@ in + "${toString (length primaryHeads)} heads set to primary: " + concatMapStringsSep ", " (x: x.output) primaryHeads; }) - { assertion = cfg.desktopManager.default == "none" && cfg.windowManager.default == "none" -> cfg.plainX; - message = "Either the desktop manager or the window manager shouldn't be `none`! " - + "To explicitly allow this, you can also set `services.xserver.plainX` to `true`. " - + "The `default` value looks for enabled WMs/DMs and select the first one."; - } ]; environment.etc = @@ -640,9 +629,7 @@ in environment = { - XORG_DRI_DRIVER_PATH = "/run/opengl-driver/lib/dri"; # !!! Depends on the driver selected at runtime. - LD_LIBRARY_PATH = concatStringsSep ":" ( - [ "${xorg.libX11.out}/lib" "${xorg.libXext.out}/lib" "/run/opengl-driver/lib" ] + LD_LIBRARY_PATH = concatStringsSep ":" ([ "/run/opengl-driver/lib" ] ++ concatLists (catAttrs "libPath" cfg.drivers)); } // cfg.displayManager.job.environment; diff --git a/nixos/modules/system/activation/activation-script.nix b/nixos/modules/system/activation/activation-script.nix index 8c9b35fe524f..c563614caaaf 100644 --- a/nixos/modules/system/activation/activation-script.nix +++ b/nixos/modules/system/activation/activation-script.nix @@ -61,7 +61,7 @@ in apply = set: { script = '' - #! ${pkgs.stdenv.shell} + #! ${pkgs.runtimeShell} systemConfig=@out@ diff --git a/nixos/modules/system/boot/grow-partition.nix b/nixos/modules/system/boot/grow-partition.nix index c4c6d82dc5c8..1e6f9e442b67 100644 --- a/nixos/modules/system/boot/grow-partition.nix +++ b/nixos/modules/system/boot/grow-partition.nix @@ -32,8 +32,15 @@ with lib; rootDevice="${config.fileSystems."/".device}" if [ -e "$rootDevice" ]; then rootDevice="$(readlink -f "$rootDevice")" - parentDevice="$(lsblk -npo PKNAME "$rootDevice")" - TMPDIR=/run sh $(type -P growpart) "$parentDevice" "''${rootDevice#$parentDevice}" + parentDevice="$rootDevice" + while [ "''${parentDevice%[0-9]}" != "''${parentDevice}" ]; do + parentDevice="''${parentDevice%[0-9]}"; + done + partNum="''${rootDevice#''${parentDevice}}" + if [ "''${parentDevice%[0-9]p}" != "''${parentDevice}" ] && [ -b "''${parentDevice%p}" ]; then + parentDevice="''${parentDevice%p}" + fi + TMPDIR=/run sh $(type -P growpart) "$parentDevice" "$partNum" udevadm settle fi ''; diff --git a/nixos/modules/system/boot/kernel.nix b/nixos/modules/system/boot/kernel.nix index 3bd7d3558269..8ea05ed14687 100644 --- a/nixos/modules/system/boot/kernel.nix +++ b/nixos/modules/system/boot/kernel.nix @@ -77,8 +77,8 @@ in type = types.int; default = 4; description = '' - The kernel console log level. Log messages with a priority - numerically less than this will not appear on the console. + The kernel console <literal>loglevel</literal>. All Kernel Messages with a log level smaller + than this setting will be printed to the console. ''; }; diff --git a/nixos/modules/system/boot/kexec.nix b/nixos/modules/system/boot/kexec.nix index b7821f9509f1..3fc1af28f628 100644 --- a/nixos/modules/system/boot/kexec.nix +++ b/nixos/modules/system/boot/kexec.nix @@ -1,21 +1,22 @@ -{ config, pkgs, ... }: +{ config, pkgs, lib, ... }: { - environment.systemPackages = [ pkgs.kexectools ]; + config = lib.mkIf (pkgs.kexectools.meta.available) { + environment.systemPackages = [ pkgs.kexectools ]; - systemd.services."prepare-kexec" = - { description = "Preparation for kexec"; - wantedBy = [ "kexec.target" ]; - before = [ "systemd-kexec.service" ]; - unitConfig.DefaultDependencies = false; - serviceConfig.Type = "oneshot"; - path = [ pkgs.kexectools ]; - script = - '' - p=$(readlink -f /nix/var/nix/profiles/system) - if ! [ -d $p ]; then exit 1; fi - exec kexec --load $p/kernel --initrd=$p/initrd --append="$(cat $p/kernel-params) init=$p/init" - ''; - }; - -} \ No newline at end of file + systemd.services."prepare-kexec" = + { description = "Preparation for kexec"; + wantedBy = [ "kexec.target" ]; + before = [ "systemd-kexec.service" ]; + unitConfig.DefaultDependencies = false; + serviceConfig.Type = "oneshot"; + path = [ pkgs.kexectools ]; + script = + '' + p=$(readlink -f /nix/var/nix/profiles/system) + if ! [ -d $p ]; then exit 1; fi + exec kexec --load $p/kernel --initrd=$p/initrd --append="$(cat $p/kernel-params) init=$p/init" + ''; + }; + }; +} diff --git a/nixos/modules/system/boot/loader/grub/grub.nix b/nixos/modules/system/boot/loader/grub/grub.nix index 0d83391de893..e2cff1c1bd94 100644 --- a/nixos/modules/system/boot/loader/grub/grub.nix +++ b/nixos/modules/system/boot/loader/grub/grub.nix @@ -40,7 +40,7 @@ let { splashImage = f cfg.splashImage; grub = f grub; grubTarget = f (grub.grubTarget or ""); - shell = "${pkgs.stdenv.shell}"; + shell = "${pkgs.runtimeShell}"; fullName = (builtins.parseDrvName realGrub.name).name; fullVersion = (builtins.parseDrvName realGrub.name).version; grubEfi = f grubEfi; @@ -536,9 +536,9 @@ in btrfsprogs = pkgs.btrfs-progs; }; in pkgs.writeScript "install-grub.sh" ('' - #!${pkgs.stdenv.shell} + #!${pkgs.runtimeShell} set -e - export PERL5LIB=${makePerlPath (with pkgs.perlPackages; [ FileSlurp XMLLibXML XMLSAX ListCompare ])} + export PERL5LIB=${makePerlPath (with pkgs.perlPackages; [ FileSlurp XMLLibXML XMLSAX XMLSAXBase ListCompare ])} ${optionalString cfg.enableCryptodisk "export GRUB_ENABLE_CRYPTODISK=y"} '' + flip concatMapStrings cfg.mirroredBoots (args: '' ${pkgs.perl}/bin/perl ${install-grub-pl} ${grubConfig args} $@ diff --git a/nixos/modules/system/boot/loader/grub/install-grub.pl b/nixos/modules/system/boot/loader/grub/install-grub.pl index cc03e54ead63..8bd203106f55 100644 --- a/nixos/modules/system/boot/loader/grub/install-grub.pl +++ b/nixos/modules/system/boot/loader/grub/install-grub.pl @@ -182,7 +182,7 @@ sub GrubFs { # Based on the type pull in the identifier from the system my ($status, @devInfo) = runCommand("@utillinux@/bin/blkid -o export @{[$fs->device]}"); if ($status != 0) { - die "Failed to get blkid info for @{[$fs->mount]} on @{[$fs->device]}"; + die "Failed to get blkid info (returned $status) for @{[$fs->mount]} on @{[$fs->device]}"; } my @matches = join("", @devInfo) =~ m/@{[uc $fsIdentifier]}=([^\n]*)/; if ($#matches != 0) { diff --git a/nixos/modules/system/boot/loader/raspberrypi/builder.sh b/nixos/modules/system/boot/loader/raspberrypi/builder.sh index f627d093eafb..8adc8a6a7e11 100644 --- a/nixos/modules/system/boot/loader/raspberrypi/builder.sh +++ b/nixos/modules/system/boot/loader/raspberrypi/builder.sh @@ -109,11 +109,15 @@ copyForced $fwdir/bootcode.bin /boot/bootcode.bin copyForced $fwdir/fixup.dat /boot/fixup.dat copyForced $fwdir/fixup_cd.dat /boot/fixup_cd.dat copyForced $fwdir/fixup_db.dat /boot/fixup_db.dat +copyForced $fwdir/fixup_x.dat /boot/fixup_x.dat copyForced $fwdir/start.elf /boot/start.elf copyForced $fwdir/start_cd.elf /boot/start_cd.elf copyForced $fwdir/start_db.elf /boot/start_db.elf copyForced $fwdir/start_x.elf /boot/start_x.elf +# Add the config.txt +copyForced @configTxt@ /boot/config.txt + # Remove obsolete files from /boot and /boot/old. for fn in /boot/old/*linux* /boot/old/*initrd-initrd* /boot/bcm*.dtb; do if ! test "${filesCopied[$fn]}" = 1; then diff --git a/nixos/modules/system/boot/loader/raspberrypi/builder_uboot.nix b/nixos/modules/system/boot/loader/raspberrypi/builder_uboot.nix new file mode 100644 index 000000000000..47f25a9c2b1b --- /dev/null +++ b/nixos/modules/system/boot/loader/raspberrypi/builder_uboot.nix @@ -0,0 +1,34 @@ +{ config, pkgs, configTxt }: + +let + cfg = config.boot.loader.raspberryPi; + isAarch64 = pkgs.stdenv.isAarch64; + + uboot = + if cfg.version == 1 then + pkgs.ubootRaspberryPi + else if cfg.version == 2 then + pkgs.ubootRaspberryPi2 + else + if isAarch64 then + pkgs.ubootRaspberryPi3_64bit + else + pkgs.ubootRaspberryPi3_32bit; + + extlinuxConfBuilder = + import ../generic-extlinux-compatible/extlinux-conf-builder.nix { + inherit pkgs; + }; +in +pkgs.substituteAll { + src = ./builder_uboot.sh; + isExecutable = true; + inherit (pkgs) bash; + path = [pkgs.coreutils pkgs.gnused pkgs.gnugrep]; + firmware = pkgs.raspberrypifw; + inherit uboot; + inherit configTxt; + inherit extlinuxConfBuilder; + version = cfg.version; +} + diff --git a/nixos/modules/system/boot/loader/raspberrypi/builder_uboot.sh b/nixos/modules/system/boot/loader/raspberrypi/builder_uboot.sh new file mode 100644 index 000000000000..36bf15066274 --- /dev/null +++ b/nixos/modules/system/boot/loader/raspberrypi/builder_uboot.sh @@ -0,0 +1,29 @@ +#! @bash@/bin/sh -e + +copyForced() { + local src="$1" + local dst="$2" + cp $src $dst.tmp + mv $dst.tmp $dst +} + +# Call the extlinux builder +"@extlinuxConfBuilder@" "$@" + +# Add the firmware files +fwdir=@firmware@/share/raspberrypi/boot/ +copyForced $fwdir/bootcode.bin /boot/bootcode.bin +copyForced $fwdir/fixup.dat /boot/fixup.dat +copyForced $fwdir/fixup_cd.dat /boot/fixup_cd.dat +copyForced $fwdir/fixup_db.dat /boot/fixup_db.dat +copyForced $fwdir/fixup_x.dat /boot/fixup_x.dat +copyForced $fwdir/start.elf /boot/start.elf +copyForced $fwdir/start_cd.elf /boot/start_cd.elf +copyForced $fwdir/start_db.elf /boot/start_db.elf +copyForced $fwdir/start_x.elf /boot/start_x.elf + +# Add the uboot file +copyForced @uboot@/u-boot.bin /boot/u-boot-rpi.bin + +# Add the config.txt +copyForced @configTxt@ /boot/config.txt diff --git a/nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix b/nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix index f246d04284ca..f974d07da9e5 100644 --- a/nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix +++ b/nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix @@ -5,42 +5,108 @@ with lib; let cfg = config.boot.loader.raspberryPi; - builder = pkgs.substituteAll { + builderGeneric = pkgs.substituteAll { src = ./builder.sh; isExecutable = true; inherit (pkgs) bash; path = [pkgs.coreutils pkgs.gnused pkgs.gnugrep]; firmware = pkgs.raspberrypifw; version = cfg.version; + inherit configTxt; }; platform = pkgs.stdenv.platform; + builderUboot = import ./builder_uboot.nix { inherit config; inherit pkgs; inherit configTxt; }; + + builder = + if cfg.uboot.enable then + "${builderUboot} -g ${toString cfg.uboot.configurationLimit} -t ${timeoutStr} -c" + else + builderGeneric; + + blCfg = config.boot.loader; + timeoutStr = if blCfg.timeout == null then "-1" else toString blCfg.timeout; + + isAarch64 = pkgs.stdenv.isAarch64; + optional = pkgs.stdenv.lib.optionalString; + + configTxt = + pkgs.writeText "config.txt" ('' + # U-Boot used to need this to work, regardless of whether UART is actually used or not. + # TODO: check when/if this can be removed. + enable_uart=1 + + # Prevent the firmware from smashing the framebuffer setup done by the mainline kernel + # when attempting to show low-voltage or overtemperature warnings. + avoid_warnings=1 + '' + optional isAarch64 '' + # Boot in 64-bit mode. + arm_control=0x200 + '' + optional cfg.uboot.enable '' + kernel=u-boot-rpi.bin + '' + optional (cfg.firmwareConfig != null) cfg.firmwareConfig); + in { options = { - boot.loader.raspberryPi.enable = mkOption { - default = false; - type = types.bool; - description = '' - Whether to create files with the system generations in - <literal>/boot</literal>. - <literal>/boot/old</literal> will hold files from old generations. - ''; - }; + boot.loader.raspberryPi = { + enable = mkOption { + default = false; + type = types.bool; + description = '' + Whether to create files with the system generations in + <literal>/boot</literal>. + <literal>/boot/old</literal> will hold files from old generations. + ''; + }; - boot.loader.raspberryPi.version = mkOption { - default = 2; - type = types.enum [ 1 2 3 ]; - description = '' - ''; - }; + version = mkOption { + default = 2; + type = types.enum [ 1 2 3 ]; + description = '' + ''; + }; + + uboot = { + enable = mkOption { + default = false; + type = types.bool; + description = '' + Enable using uboot as bootmanager for the raspberry pi. + ''; + }; + + configurationLimit = mkOption { + default = 20; + example = 10; + type = types.int; + description = '' + Maximum number of configurations in the boot menu. + ''; + }; + + }; + firmwareConfig = mkOption { + default = null; + type = types.nullOr types.string; + description = '' + Extra options that will be appended to <literal>/boot/config.txt</literal> file. + For possible values, see: https://www.raspberrypi.org/documentation/configuration/config-txt/ + ''; + }; + }; }; - config = mkIf config.boot.loader.raspberryPi.enable { + config = mkIf cfg.enable { + assertions = singleton { + assertion = !pkgs.stdenv.isAarch64 || cfg.version == 3; + message = "Only Raspberry Pi 3 supports aarch64."; + }; + system.build.installBootLoader = builder; system.boot.loader.id = "raspberrypi"; system.boot.loader.kernelFile = platform.kernelTarget; diff --git a/nixos/modules/system/boot/networkd.nix b/nixos/modules/system/boot/networkd.nix index eea10613ea58..9aa557ac8595 100644 --- a/nixos/modules/system/boot/networkd.nix +++ b/nixos/modules/system/boot/networkd.nix @@ -146,12 +146,13 @@ let # .network files have a [Link] section with different options than in .netlink files checkNetworkLink = checkUnitConfig "Link" [ (assertOnlyFields [ - "MACAddress" "MTUBytes" "ARP" "Unmanaged" + "MACAddress" "MTUBytes" "ARP" "Unmanaged" "RequiredForOnline" ]) (assertMacAddress "MACAddress") (assertByteFormat "MTUBytes") (assertValueOneOf "ARP" boolValues) (assertValueOneOf "Unmanaged" boolValues) + (assertValueOneOf "RquiredForOnline" boolValues) ]; @@ -712,6 +713,9 @@ in systemd.services.systemd-networkd = { wantedBy = [ "multi-user.target" ]; restartTriggers = map (f: f.source) (unitFiles); + # prevent race condition with interface renaming (#39069) + requires = [ "systemd-udev-settle.service" ]; + after = [ "systemd-udev-settle.service" ]; }; systemd.services.systemd-networkd-wait-online = { diff --git a/nixos/modules/system/boot/stage-1.nix b/nixos/modules/system/boot/stage-1.nix index df450be8c401..55bb6d3449c5 100644 --- a/nixos/modules/system/boot/stage-1.nix +++ b/nixos/modules/system/boot/stage-1.nix @@ -30,6 +30,50 @@ let # mounting `/`, like `/` on a loopback). fileSystems = filter utils.fsNeededForBoot config.system.build.fileSystems; + # A utility for enumerating the shared-library dependencies of a program + findLibs = pkgs.writeShellScriptBin "find-libs" '' + set -euo pipefail + + declare -A seen + declare -a left + + patchelf="${pkgs.buildPackages.patchelf}/bin/patchelf" + + function add_needed { + rpath="$($patchelf --print-rpath $1)" + dir="$(dirname $1)" + for lib in $($patchelf --print-needed $1); do + left+=("$lib" "$rpath" "$dir") + done + } + + add_needed $1 + + while [ ''${#left[@]} -ne 0 ]; do + next=''${left[0]} + rpath=''${left[1]} + ORIGIN=''${left[2]} + left=("''${left[@]:3}") + if [ -z ''${seen[$next]+x} ]; then + seen[$next]=1 + IFS=: read -ra paths <<< $rpath + res= + for path in "''${paths[@]}"; do + path=$(eval "echo $path") + if [ -f "$path/$next" ]; then + res="$path/$next" + echo "$res" + add_needed "$res" + break + fi + done + if [ -z "$res" ]; then + echo "Couldn't satisfy dependency $next" >&2 + exit 1 + fi + fi + done + ''; # Some additional utilities needed in stage 1, like mount, lvm, fsck # etc. We don't want to bring in all of those packages, so we just @@ -37,7 +81,7 @@ let # we just copy what we need from Glibc and use patchelf to make it # work. extraUtils = pkgs.runCommandCC "extra-utils" - { buildInputs = [pkgs.nukeReferences]; + { nativeBuildInputs = [pkgs.buildPackages.nukeReferences]; allowedReferences = [ "out" ]; # prevent accidents like glibc being included in the initrd } '' @@ -103,9 +147,7 @@ let # Copy all of the needed libraries find $out/bin $out/lib -type f | while read BIN; do echo "Copying libs for executable $BIN" - LDD="$(ldd $BIN)" || continue - LIBS="$(echo "$LDD" | awk '{print $3}' | sed '/^$/d')" - for LIB in $LIBS; do + for LIB in $(${findLibs}/bin/find-libs $BIN); do TGT="$out/lib/$(basename $LIB)" if [ ! -f "$TGT" ]; then SRC="$(readlink -e $LIB)" @@ -132,6 +174,7 @@ let fi done + if [ -z "${toString pkgs.stdenv.isCross}" ]; then # Make sure that the patchelf'ed binaries still work. echo "testing patched programs..." $out/bin/ash -c 'echo hello world' | grep "hello world" @@ -144,6 +187,7 @@ let $out/bin/mdadm --version ${config.boot.initrd.extraUtilsCommandsTest} + fi ''; # */ @@ -245,7 +289,7 @@ let { src = "${pkgs.kmod-blacklist-ubuntu}/modprobe.conf"; } '' target=$out - ${pkgs.perl}/bin/perl -0pe 's/## file: iwlwifi.conf(.+?)##/##/s;' $src > $out + ${pkgs.buildPackages.perl}/bin/perl -0pe 's/## file: iwlwifi.conf(.+?)##/##/s;' $src > $out ''; symlink = "/etc/modprobe.d/ubuntu.conf"; } diff --git a/nixos/modules/system/boot/stage-2-init.sh b/nixos/modules/system/boot/stage-2-init.sh index 9d2c580d62a7..b83012dfda7e 100644 --- a/nixos/modules/system/boot/stage-2-init.sh +++ b/nixos/modules/system/boot/stage-2-init.sh @@ -43,7 +43,7 @@ if [ ! -e /proc/1 ]; then local options="$3" local fsType="$4" - mkdir -m 0755 -p "$mountPoint" + install -m 0755 -d "$mountPoint" mount -n -t "$fsType" -o "$options" "$device" "$mountPoint" } source @earlyMountScript@ @@ -71,7 +71,7 @@ fi # Provide a /etc/mtab. -mkdir -m 0755 -p /etc +install -m 0755 -d /etc test -e /etc/fstab || touch /etc/fstab # to shut up mount rm -f /etc/mtab* # not that we care about stale locks ln -s /proc/mounts /etc/mtab @@ -79,8 +79,8 @@ ln -s /proc/mounts /etc/mtab # More special file systems, initialise required directories. [ -e /proc/bus/usb ] && mount -t usbfs usbfs /proc/bus/usb # UML doesn't have USB by default -mkdir -m 01777 -p /tmp -mkdir -m 0755 -p /var/{log,lib,db} /nix/var /etc/nixos/ \ +install -m 01777 -d /tmp +install -m 0755 -d /var/{log,lib,db} /nix/var /etc/nixos/ \ /run/lock /home /bin # for the /bin/sh symlink diff --git a/nixos/modules/system/boot/stage-2.nix b/nixos/modules/system/boot/stage-2.nix index 8db6d2d2f734..78afbd8dbc12 100644 --- a/nixos/modules/system/boot/stage-2.nix +++ b/nixos/modules/system/boot/stage-2.nix @@ -10,6 +10,7 @@ let bootStage2 = pkgs.substituteAll { src = ./stage-2-init.sh; shellDebug = "${pkgs.bashInteractive}/bin/bash"; + shell = "${pkgs.bash}/bin/bash"; isExecutable = true; inherit (config.nix) readOnlyStore; inherit (config.networking) useHostResolvConf; diff --git a/nixos/modules/system/boot/systemd-nspawn.nix b/nixos/modules/system/boot/systemd-nspawn.nix index 8fa9f8b795e5..64b3b8b584e3 100644 --- a/nixos/modules/system/boot/systemd-nspawn.nix +++ b/nixos/modules/system/boot/systemd-nspawn.nix @@ -110,7 +110,7 @@ in { config = let - units = mapAttrs' (n: v: nameValuePair "${n}.nspawn" (instanceToUnit n v)) cfg; + units = mapAttrs' (n: v: let nspawnFile = "${n}.nspawn"; in nameValuePair nspawnFile (instanceToUnit nspawnFile v)) cfg; in mkIf (cfg != {}) { environment.etc."systemd/nspawn".source = generateUnits "nspawn" units [] []; diff --git a/nixos/modules/system/boot/systemd-unit-options.nix b/nixos/modules/system/boot/systemd-unit-options.nix index 43a9c28bb694..5255f1a1b97a 100644 --- a/nixos/modules/system/boot/systemd-unit-options.nix +++ b/nixos/modules/system/boot/systemd-unit-options.nix @@ -217,7 +217,7 @@ in rec { environment = mkOption { default = {}; - type = types.attrs; # FIXME + type = with types; attrsOf (nullOr (either str package)); example = { PATH = "/foo/bar/bin"; LANG = "nl_NL.UTF-8"; }; description = "Environment variables passed to the service's processes."; }; diff --git a/nixos/modules/system/boot/systemd.nix b/nixos/modules/system/boot/systemd.nix index aff46ea861a2..d2fe33488a7a 100644 --- a/nixos/modules/system/boot/systemd.nix +++ b/nixos/modules/system/boot/systemd.nix @@ -137,7 +137,6 @@ let # Slices / containers. "slices.target" - "system.slice" "user.slice" "machine.slice" "machines.target" @@ -241,37 +240,37 @@ let } (mkIf (config.preStart != "") { serviceConfig.ExecStartPre = makeJobScript "${name}-pre-start" '' - #! ${pkgs.stdenv.shell} -e + #! ${pkgs.runtimeShell} -e ${config.preStart} ''; }) (mkIf (config.script != "") { serviceConfig.ExecStart = makeJobScript "${name}-start" '' - #! ${pkgs.stdenv.shell} -e + #! ${pkgs.runtimeShell} -e ${config.script} '' + " " + config.scriptArgs; }) (mkIf (config.postStart != "") { serviceConfig.ExecStartPost = makeJobScript "${name}-post-start" '' - #! ${pkgs.stdenv.shell} -e + #! ${pkgs.runtimeShell} -e ${config.postStart} ''; }) (mkIf (config.reload != "") { serviceConfig.ExecReload = makeJobScript "${name}-reload" '' - #! ${pkgs.stdenv.shell} -e + #! ${pkgs.runtimeShell} -e ${config.reload} ''; }) (mkIf (config.preStop != "") { serviceConfig.ExecStop = makeJobScript "${name}-pre-stop" '' - #! ${pkgs.stdenv.shell} -e + #! ${pkgs.runtimeShell} -e ${config.preStop} ''; }) (mkIf (config.postStop != "") { serviceConfig.ExecStopPost = makeJobScript "${name}-post-stop" '' - #! ${pkgs.stdenv.shell} -e + #! ${pkgs.runtimeShell} -e ${config.postStop} ''; }) @@ -516,7 +515,7 @@ in }; systemd.globalEnvironment = mkOption { - type = types.attrs; + type = with types; attrsOf (nullOr (either str package)); default = {}; example = { TZ = "CET"; }; description = '' @@ -524,6 +523,14 @@ in ''; }; + systemd.enableCgroupAccounting = mkOption { + default = false; + type = types.bool; + description = '' + Whether to enable cgroup accounting. + ''; + }; + systemd.extraConfig = mkOption { default = ""; type = types.lines; @@ -725,6 +732,13 @@ in "systemd/system.conf".text = '' [Manager] + ${optionalString config.systemd.enableCgroupAccounting '' + DefaultCPUAccounting=yes + DefaultIOAccounting=yes + DefaultBlockIOAccounting=yes + DefaultMemoryAccounting=yes + DefaultTasksAccounting=yes + ''} ${config.systemd.extraConfig} ''; @@ -821,7 +835,8 @@ in system.requiredKernelConfig = map config.lib.kernelConfig.isEnabled [ "DEVTMPFS" "CGROUPS" "INOTIFY_USER" "SIGNALFD" "TIMERFD" "EPOLL" "NET" - "SYSFS" "PROC_FS" "FHANDLE" "DMIID" "AUTOFS4_FS" "TMPFS_POSIX_ACL" + "SYSFS" "PROC_FS" "FHANDLE" "CRYPTO_USER_API_HASH" "CRYPTO_HMAC" + "CRYPTO_SHA256" "DMIID" "AUTOFS4_FS" "TMPFS_POSIX_ACL" "TMPFS_XATTR" "SECCOMP" ]; diff --git a/nixos/modules/tasks/filesystems/exfat.nix b/nixos/modules/tasks/filesystems/exfat.nix index 963bc940b4fa..1527f993fdd4 100644 --- a/nixos/modules/tasks/filesystems/exfat.nix +++ b/nixos/modules/tasks/filesystems/exfat.nix @@ -5,7 +5,7 @@ with lib; { config = mkIf (any (fs: fs == "exfat") config.boot.supportedFilesystems) { - system.fsPackages = [ pkgs.exfat-utils pkgs.fuse_exfat ]; + system.fsPackages = [ pkgs.exfat ]; }; } diff --git a/nixos/modules/tasks/filesystems/zfs.nix b/nixos/modules/tasks/filesystems/zfs.nix index 30c54ddd0e4e..c3bf897d51fd 100644 --- a/nixos/modules/tasks/filesystems/zfs.nix +++ b/nixos/modules/tasks/filesystems/zfs.nix @@ -305,6 +305,8 @@ in } ]; + virtualisation.lxd.zfsSupport = true; + boot = { kernelModules = [ "spl" "zfs" ] ; extraModulePackages = with packages; [ spl zfs ]; @@ -452,7 +454,7 @@ in }) snapshotNames); systemd.timers = let - timer = name: if name == "frequent" then "*:15,30,45" else name; + timer = name: if name == "frequent" then "*:0,15,30,45" else name; in builtins.listToAttrs (map (snapName: { name = "zfs-snapshot-${snapName}"; diff --git a/nixos/modules/tasks/kbd.nix b/nixos/modules/tasks/kbd.nix index 7fb3cbc5c1bc..fbe42b8e8f04 100644 --- a/nixos/modules/tasks/kbd.nix +++ b/nixos/modules/tasks/kbd.nix @@ -13,7 +13,7 @@ let isUnicode = hasSuffix "UTF-8" (toUpper config.i18n.defaultLocale); optimizedKeymap = pkgs.runCommand "keymap" { - nativeBuildInputs = [ pkgs.kbd ]; + nativeBuildInputs = [ pkgs.buildPackages.kbd ]; LOADKEYS_KEYMAP_PATH = "${kbdEnv}/share/keymaps/**"; } '' loadkeys -b ${optionalString isUnicode "-u"} "${config.i18n.consoleKeyMap}" > $out diff --git a/nixos/modules/tasks/network-interfaces-scripted.nix b/nixos/modules/tasks/network-interfaces-scripted.nix index 630fe6d114ce..e754a1e8718d 100644 --- a/nixos/modules/tasks/network-interfaces-scripted.nix +++ b/nixos/modules/tasks/network-interfaces-scripted.nix @@ -68,8 +68,7 @@ let (hasAttr dev cfg.macvlans) || (hasAttr dev cfg.sits) || (hasAttr dev cfg.vlans) || - (hasAttr dev cfg.vswitches) || - (hasAttr dev cfg.wlanInterfaces) + (hasAttr dev cfg.vswitches) then [ "${dev}-netdev.service" ] else optional (dev != null && dev != "lo" && !config.boot.isContainer) (subsystemDevice dev); @@ -192,7 +191,7 @@ let if out=$(ip addr add "${cidr}" dev "${i.name}" 2>&1); then echo "done" elif ! echo "$out" | grep "File exists" >/dev/null 2>&1; then - echo "failed" + echo "'ip addr add "${cidr}" dev "${i.name}"' failed: $out" exit 1 fi '' @@ -213,7 +212,7 @@ let if out=$(ip route add "${cidr}" ${options} ${via} dev "${i.name}" 2>&1); then echo "done" elif ! echo "$out" | grep "File exists" >/dev/null 2>&1; then - echo "failed" + echo "'ip route add "${cidr}" ${options} ${via} dev "${i.name}"' failed: $out" exit 1 fi '' diff --git a/nixos/modules/tasks/network-interfaces.nix b/nixos/modules/tasks/network-interfaces.nix index 5036b701bd86..14f9b9567515 100644 --- a/nixos/modules/tasks/network-interfaces.nix +++ b/nixos/modules/tasks/network-interfaces.nix @@ -26,7 +26,7 @@ let executable = true; destination = "/bin/bridge-stp"; text = '' - #!${pkgs.stdenv.shell} -e + #!${pkgs.runtimeShell} -e export PATH="${pkgs.mstpd}/bin" BRIDGES=(${concatStringsSep " " (attrNames rstpBridges)}) @@ -62,35 +62,6 @@ let then mapAttrsToList (n: v: v//{_iName=n;}) (filterAttrs (n: _: n==device) interfaces) ++ mapAttrsToList (n: v: v//{_iName=n;}) (filterAttrs (n: _: n!=device) interfaces) else mapAttrsToList (n: v: v // {_iName = n;}) interfaces; - # udev script that configures a physical wlan device and adds virtual interfaces - wlanDeviceUdevScript = device: interfaceList: pkgs.writeScript "wlan-${device}-udev-script" '' - #!${pkgs.stdenv.shell} - - # Change the wireless phy device to a predictable name. - if [ -e "/sys/class/net/${device}/phy80211/name" ]; then - ${pkgs.iw}/bin/iw phy `${pkgs.coreutils}/bin/cat /sys/class/net/${device}/phy80211/name` set name ${device} || true - fi - - # Crate new, virtual interfaces and configure them at the same time - ${flip concatMapStrings (drop 1 interfaceList) (i: '' - ${pkgs.iw}/bin/iw dev ${device} interface add ${i._iName} type ${i.type} \ - ${optionalString (i.type == "mesh" && i.meshID != null) "mesh_id ${i.meshID}"} \ - ${optionalString (i.type == "monitor" && i.flags != null) "flags ${i.flags}"} \ - ${optionalString (i.type == "managed" && i.fourAddr != null) "4addr ${if i.fourAddr then "on" else "off"}"} \ - ${optionalString (i.mac != null) "addr ${i.mac}"} - '')} - - # Reconfigure and rename the default interface that already exists - ${flip concatMapStrings (take 1 interfaceList) (i: '' - ${pkgs.iw}/bin/iw dev ${device} set type ${i.type} - ${optionalString (i.type == "mesh" && i.meshID != null) "${pkgs.iw}/bin/iw dev ${device} set meshid ${i.meshID}"} - ${optionalString (i.type == "monitor" && i.flags != null) "${pkgs.iw}/bin/iw dev ${device} set monitor ${i.flags}"} - ${optionalString (i.type == "managed" && i.fourAddr != null) "${pkgs.iw}/bin/iw dev ${device} set 4addr ${if i.fourAddr then "on" else "off"}"} - ${optionalString (i.mac != null) "${pkgs.iproute}/bin/ip link set dev ${device} address ${i.mac}"} - ${optionalString (device != i._iName) "${pkgs.iproute}/bin/ip link set dev ${device} name ${i._iName}"} - '')} - ''; - # We must escape interfaces due to the systemd interpretation subsystemDevice = interface: "sys-subsystem-net-devices-${escapeSystemdPath interface}.device"; @@ -142,7 +113,7 @@ let default = { }; example = { mtu = "1492"; window = "524288"; }; description = '' - Other route options. See the symbol <literal>OPTION</literal> + Other route options. See the symbol <literal>OPTIONS</literal> in the <literal>ip-route(8)</literal> manual page for the details. ''; }; @@ -191,7 +162,7 @@ let preferTempAddress = mkOption { type = types.bool; default = cfg.enableIPv6; - defaultText = literalExample "config.networking.enableIpv6"; + defaultText = literalExample "config.networking.enableIPv6"; description = '' When using SLAAC prefer a temporary (IPv6) address over the EUI-64 address for originating connections. This is used to reduce tracking. @@ -489,7 +460,7 @@ in networking.interfaces = mkOption { default = {}; example = - { eth0.ipv4 = [ { + { eth0.ipv4.addresses = [ { address = "131.211.84.78"; prefixLength = 25; } ]; @@ -1158,7 +1129,7 @@ in # The script creates the required, new WLAN interfaces interfaces and configures the # existing, default interface. curInterfaceScript = device: current: new: pkgs.writeScript "udev-run-script-wlan-interfaces-${device}.sh" '' - #!${pkgs.stdenv.shell} + #!${pkgs.runtimeShell} # Change the wireless phy device to a predictable name. ${pkgs.iw}/bin/iw phy `${pkgs.coreutils}/bin/cat /sys/class/net/$INTERFACE/phy80211/name` set name ${device} @@ -1177,7 +1148,7 @@ in # Udev script to execute for a new WLAN interface. The script configures the new WLAN interface. newInterfaceScript = device: new: pkgs.writeScript "udev-run-script-wlan-interfaces-${new._iName}.sh" '' - #!${pkgs.stdenv.shell} + #!${pkgs.runtimeShell} # Configure the new interface ${pkgs.iw}/bin/iw dev ${new._iName} set type ${new.type} ${optionalString (new.type == "mesh" && new.meshID!=null) "${pkgs.iw}/bin/iw dev ${device} set meshid ${new.meshID}"} diff --git a/nixos/modules/virtualisation/amazon-init.nix b/nixos/modules/virtualisation/amazon-init.nix index a7362423eb46..8032b2c6d7ca 100644 --- a/nixos/modules/virtualisation/amazon-init.nix +++ b/nixos/modules/virtualisation/amazon-init.nix @@ -2,7 +2,7 @@ let script = '' - #!${pkgs.stdenv.shell} -eu + #!${pkgs.runtimeShell} -eu echo "attempting to fetch configuration from EC2 user data..." diff --git a/nixos/modules/virtualisation/azure-agent.nix b/nixos/modules/virtualisation/azure-agent.nix index 6817eb837a01..b7ab54aab7ec 100644 --- a/nixos/modules/virtualisation/azure-agent.nix +++ b/nixos/modules/virtualisation/azure-agent.nix @@ -47,7 +47,7 @@ let }; provisionedHook = pkgs.writeScript "provisioned-hook" '' - #!${pkgs.stdenv.shell} + #!${pkgs.runtimeShell} ${config.systemd.package}/bin/systemctl start provisioned.target ''; @@ -66,6 +66,10 @@ in default = false; description = "Whether to enable verbose logging."; }; + mountResourceDisk = mkOption { + default = true; + description = "Whether the agent should format (ext4) and mount the resource disk to /mnt/resource."; + }; }; ###### implementation @@ -112,7 +116,7 @@ in Provisioning.ExecuteCustomData=n # Format if unformatted. If 'n', resource disk will not be mounted. - ResourceDisk.Format=y + ResourceDisk.Format=${if cfg.mountResourceDisk then "y" else "n"} # File system on the resource disk # Typically ext3 or ext4. FreeBSD images should use 'ufs2' here. @@ -181,7 +185,7 @@ in after = [ "network-online.target" "sshd.service" ]; wants = [ "network-online.target" ]; - path = [ pkgs.e2fsprogs ]; + path = [ pkgs.e2fsprogs pkgs.bash ]; description = "Windows Azure Agent Service"; unitConfig.ConditionPathExists = "/etc/waagent.conf"; serviceConfig = { diff --git a/nixos/modules/virtualisation/containers.nix b/nixos/modules/virtualisation/containers.nix index 4038454b2d2f..248c2fc1fb23 100644 --- a/nixos/modules/virtualisation/containers.nix +++ b/nixos/modules/virtualisation/containers.nix @@ -33,7 +33,7 @@ let in pkgs.writeScript "container-init" '' - #! ${pkgs.stdenv.shell} -e + #! ${pkgs.runtimeShell} -e # Initialise the container side of the veth pair. if [ "$PRIVATE_NETWORK" = 1 ]; then @@ -112,7 +112,7 @@ let # If the host is 64-bit and the container is 32-bit, add a # --personality flag. - ${optionalString (config.nixpkgs.system == "x86_64-linux") '' + ${optionalString (config.nixpkgs.localSystem.system == "x86_64-linux") '' if [ "$(< ''${SYSTEM_PATH:-/nix/var/nix/profiles/per-container/$INSTANCE/system}/system)" = i686-linux ]; then extraFlags+=" --personality=x86" fi @@ -223,7 +223,7 @@ let serviceDirectives = cfg: { ExecReload = pkgs.writeScript "reload-container" '' - #! ${pkgs.stdenv.shell} -e + #! ${pkgs.runtimeShell} -e ${pkgs.nixos-container}/bin/nixos-container run "$INSTANCE" -- \ bash --login -c "''${SYSTEM_PATH:-/nix/var/nix/profiles/system}/bin/switch-to-configuration test" ''; @@ -255,7 +255,7 @@ let }; - system = config.nixpkgs.system; + system = config.nixpkgs.localSystem.system; bindMountOpts = { name, config, ... }: { @@ -575,6 +575,16 @@ in ''; }; + extraFlags = mkOption { + type = types.listOf types.str; + default = []; + example = [ "--drop-capability=CAP_SYS_CHROOT" ]; + description = '' + Extra flags passed to the systemd-nspawn command. + See systemd-nspawn(1) for details. + ''; + }; + } // networkOptions; config = mkMerge @@ -714,7 +724,9 @@ in ${optionalString cfg.autoStart '' AUTO_START=1 ''} - EXTRA_NSPAWN_FLAGS="${mkBindFlags cfg.bindMounts}" + EXTRA_NSPAWN_FLAGS="${mkBindFlags cfg.bindMounts + + optionalString (cfg.extraFlags != []) + (" " + concatStringsSep " " cfg.extraFlags)}" ''; }) config.containers; diff --git a/nixos/modules/virtualisation/ec2-amis.nix b/nixos/modules/virtualisation/ec2-amis.nix index 01512911a057..baffad79b001 100644 --- a/nixos/modules/virtualisation/ec2-amis.nix +++ b/nixos/modules/virtualisation/ec2-amis.nix @@ -240,5 +240,22 @@ let self = { "17.09".sa-east-1.hvm-ebs = "ami-4762202b"; "17.09".ap-south-1.hvm-ebs = "ami-4e376021"; - latest = self."17.09"; + # 18.03.131792.becbe4dbe16 + "18.03".eu-west-1.hvm-ebs = "ami-cda4fab4"; + "18.03".eu-west-2.hvm-ebs = "ami-d96786be"; + "18.03".eu-west-3.hvm-ebs = "ami-6b0cba16"; + "18.03".eu-central-1.hvm-ebs = "ami-5e2b75b5"; + "18.03".us-east-1.hvm-ebs = "ami-d464cba9"; + "18.03".us-east-2.hvm-ebs = "ami-fd221298"; + "18.03".us-west-1.hvm-ebs = "ami-ff0d1d9f"; + "18.03".us-west-2.hvm-ebs = "ami-c05c3bb8"; + "18.03".ca-central-1.hvm-ebs = "ami-cc72f4a8"; + "18.03".ap-southeast-1.hvm-ebs = "ami-b61633ca"; + "18.03".ap-southeast-2.hvm-ebs = "ami-530fc131"; + "18.03".ap-northeast-1.hvm-ebs = "ami-90d6c0ec"; + "18.03".ap-northeast-2.hvm-ebs = "ami-a1248bcf"; + "18.03".sa-east-1.hvm-ebs = "ami-b090c6dc"; + "18.03".ap-south-1.hvm-ebs = "ami-32c9ec5d"; + + latest = self."18.03"; }; in self diff --git a/nixos/modules/virtualisation/google-compute-image.nix b/nixos/modules/virtualisation/google-compute-image.nix index 155a33b3bb37..0b6bec786da4 100644 --- a/nixos/modules/virtualisation/google-compute-image.nix +++ b/nixos/modules/virtualisation/google-compute-image.nix @@ -2,7 +2,7 @@ with lib; let - diskSize = 1024; # MB + diskSize = 1536; # MB gce = pkgs.google-compute-engine; in { @@ -57,6 +57,12 @@ in # Always include cryptsetup so that NixOps can use it. environment.systemPackages = [ pkgs.cryptsetup ]; + # Make sure GCE image does not replace host key that NixOps sets + environment.etc."default/instance_configs.cfg".text = lib.mkDefault '' + [InstanceSetup] + set_host_keys = false + ''; + # Rely on GCP's firewall instead networking.firewall.enable = mkDefault false; @@ -69,6 +75,9 @@ in networking.usePredictableInterfaceNames = false; + # GC has 1460 MTU + networking.interfaces.eth0.mtu = 1460; + # allow the google-accounts-daemon to manage users users.mutableUsers = true; # and allow users to sudo without password diff --git a/nixos/modules/virtualisation/libvirtd.nix b/nixos/modules/virtualisation/libvirtd.nix index a369b7ddbe1d..024db7f87c2e 100644 --- a/nixos/modules/virtualisation/libvirtd.nix +++ b/nixos/modules/virtualisation/libvirtd.nix @@ -119,18 +119,10 @@ in { after = [ "systemd-udev-settle.service" ] ++ optional vswitch.enable "vswitchd.service"; - environment = { - LIBVIRTD_ARGS = ''--config "${configFile}" ${concatStringsSep " " cfg.extraOptions}''; - }; + environment.LIBVIRTD_ARGS = ''--config "${configFile}" ${concatStringsSep " " cfg.extraOptions}''; - path = with pkgs; [ - bridge-utils - dmidecode - dnsmasq - ebtables - cfg.qemuPackage # libvirtd requires qemu-img to manage disk images - ] - ++ optional vswitch.enable vswitch.package; + path = [ cfg.qemuPackage ] # libvirtd requires qemu-img to manage disk images + ++ optional vswitch.enable vswitch.package; preStart = '' mkdir -p /var/log/libvirt/qemu -m 755 diff --git a/nixos/modules/virtualisation/lxc.nix b/nixos/modules/virtualisation/lxc.nix index 2310fe984325..9b5adaf08249 100644 --- a/nixos/modules/virtualisation/lxc.nix +++ b/nixos/modules/virtualisation/lxc.nix @@ -74,6 +74,9 @@ in systemd.tmpfiles.rules = [ "d /var/lib/lxc/rootfs 0755 root root -" ]; security.apparmor.packages = [ pkgs.lxc ]; - security.apparmor.profiles = [ "${pkgs.lxc}/etc/apparmor.d/lxc-containers" ]; + security.apparmor.profiles = [ + "${pkgs.lxc}/etc/apparmor.d/lxc-containers" + "${pkgs.lxc}/etc/apparmor.d/usr.bin.lxc-start" + ]; }; } diff --git a/nixos/modules/virtualisation/lxd.nix b/nixos/modules/virtualisation/lxd.nix index 4988886baf60..3e76cdacfc4b 100644 --- a/nixos/modules/virtualisation/lxd.nix +++ b/nixos/modules/virtualisation/lxd.nix @@ -15,28 +15,34 @@ in options = { - virtualisation.lxd.enable = - mkOption { + virtualisation.lxd = { + enable = mkOption { type = types.bool; default = false; - description = - '' - This option enables lxd, a daemon that manages - containers. Users in the "lxd" group can interact with - the daemon (e.g. to start or stop containers) using the - <command>lxc</command> command line tool, among others. - ''; + description = '' + This option enables lxd, a daemon that manages + containers. Users in the "lxd" group can interact with + the daemon (e.g. to start or stop containers) using the + <command>lxc</command> command line tool, among others. + ''; }; - + zfsSupport = mkOption { + type = types.bool; + default = false; + description = '' + enables lxd to use zfs as a storage for containers. + This option is enabled by default if a zfs pool is configured + with nixos. + ''; + }; + }; }; - ###### implementation config = mkIf cfg.enable { - environment.systemPackages = - [ pkgs.lxd ]; + environment.systemPackages = [ pkgs.lxd ]; security.apparmor = { enable = true; @@ -47,31 +53,31 @@ in packages = [ pkgs.lxc ]; }; - systemd.services.lxd = - { description = "LXD Container Management Daemon"; + systemd.services.lxd = { + description = "LXD Container Management Daemon"; - wantedBy = [ "multi-user.target" ]; - after = [ "systemd-udev-settle.service" ]; + wantedBy = [ "multi-user.target" ]; + after = [ "systemd-udev-settle.service" ]; - # TODO(wkennington): Add lvm2 and thin-provisioning-tools - path = with pkgs; [ acl rsync gnutar xz btrfs-progs gzip dnsmasq squashfsTools iproute iptables ]; + path = lib.optional cfg.zfsSupport pkgs.zfs; - preStart = '' - mkdir -m 0755 -p /var/lib/lxc/rootfs - ''; + preStart = '' + mkdir -m 0755 -p /var/lib/lxc/rootfs + ''; - serviceConfig.ExecStart = "@${pkgs.lxd.bin}/bin/lxd lxd --syslog --group lxd"; - serviceConfig.Type = "simple"; - serviceConfig.KillMode = "process"; # when stopping, leave the containers alone + serviceConfig = { + ExecStart = "@${pkgs.lxd.bin}/bin/lxd lxd --group lxd"; + Type = "simple"; + KillMode = "process"; # when stopping, leave the containers alone }; + }; + users.extraGroups.lxd.gid = config.ids.gids.lxd; users.extraUsers.root = { subUidRanges = [ { startUid = 1000000; count = 65536; } ]; subGidRanges = [ { startGid = 1000000; count = 65536; } ]; }; - }; - } diff --git a/nixos/modules/virtualisation/openvswitch.nix b/nixos/modules/virtualisation/openvswitch.nix index 4218a3840fc1..38b138e06326 100644 --- a/nixos/modules/virtualisation/openvswitch.nix +++ b/nixos/modules/virtualisation/openvswitch.nix @@ -169,7 +169,7 @@ in { mkdir -p ${runDir}/ipsec/{etc/racoon,etc/init.d/,usr/sbin/} ln -fs ${pkgs.ipsecTools}/bin/setkey ${runDir}/ipsec/usr/sbin/setkey ln -fs ${pkgs.writeScript "racoon-restart" '' - #!${pkgs.stdenv.shell} + #!${pkgs.runtimeShell} /var/run/current-system/sw/bin/systemctl $1 racoon ''} ${runDir}/ipsec/etc/init.d/racoon ''; diff --git a/nixos/modules/virtualisation/qemu-vm.nix b/nixos/modules/virtualisation/qemu-vm.nix index 13d0eb7de5c2..66ff43c8547d 100644 --- a/nixos/modules/virtualisation/qemu-vm.nix +++ b/nixos/modules/virtualisation/qemu-vm.nix @@ -27,10 +27,25 @@ let kernelConsole = if cfg.graphics then "" else "console=${qemuSerialDevice}"; ttys = [ "tty1" "tty2" "tty3" "tty4" "tty5" "tty6" ]; + # XXX: This is very ugly and in the future we really should use attribute + # sets to build ALL of the QEMU flags instead of this mixed mess of Nix + # expressions and shell script stuff. + mkDiskIfaceDriveFlag = idx: driveArgs: let + inherit (cfg.qemu) diskInterface; + # The drive identifier created by incrementing the index by one using the + # shell. + drvId = "drive$((${idx} + 1))"; + # NOTE: DO NOT shell escape, because this may contain shell variables. + commonArgs = "index=${idx},id=${drvId},${driveArgs}"; + isSCSI = diskInterface == "scsi"; + devArgs = "${diskInterface}-hd,drive=${drvId}"; + args = "-drive ${commonArgs},if=none -device lsi53c895a -device ${devArgs}"; + in if isSCSI then args else "-drive ${commonArgs},if=${diskInterface}"; + # Shell script to start the VM. startVM = '' - #! ${pkgs.stdenv.shell} + #! ${pkgs.runtimeShell} NIX_DISK_IMAGE=$(readlink -f ''${NIX_DISK_IMAGE:-${config.virtualisation.diskImage}}) @@ -68,7 +83,7 @@ let if ! test -e "empty$idx.qcow2"; then ${qemu}/bin/qemu-img create -f qcow2 "empty$idx.qcow2" "${toString size}M" fi - extraDisks="$extraDisks -drive index=$idx,file=$(pwd)/empty$idx.qcow2,if=${cfg.qemu.diskInterface},werror=report" + extraDisks="$extraDisks ${mkDiskIfaceDriveFlag "$idx" "file=$(pwd)/empty$idx.qcow2,werror=report"}" idx=$((idx + 1)) '')} @@ -77,19 +92,20 @@ let -name ${vmName} \ -m ${toString config.virtualisation.memorySize} \ -smp ${toString config.virtualisation.cores} \ + -device virtio-rng-pci \ ${concatStringsSep " " config.virtualisation.qemu.networkingOptions} \ -virtfs local,path=/nix/store,security_model=none,mount_tag=store \ -virtfs local,path=$TMPDIR/xchg,security_model=none,mount_tag=xchg \ -virtfs local,path=''${SHARED_DIR:-$TMPDIR/xchg},security_model=none,mount_tag=shared \ ${if cfg.useBootLoader then '' - -drive index=0,id=drive1,file=$NIX_DISK_IMAGE,if=${cfg.qemu.diskInterface},cache=writeback,werror=report \ - -drive index=1,id=drive2,file=$TMPDIR/disk.img,media=disk \ + ${mkDiskIfaceDriveFlag "0" "file=$NIX_DISK_IMAGE,cache=writeback,werror=report"} \ + ${mkDiskIfaceDriveFlag "1" "file=$TMPDIR/disk.img,media=disk"} \ ${if cfg.useEFIBoot then '' -pflash $TMPDIR/bios.bin \ '' else '' ''} '' else '' - -drive index=0,id=drive1,file=$NIX_DISK_IMAGE,if=${cfg.qemu.diskInterface},cache=writeback,werror=report \ + ${mkDiskIfaceDriveFlag "0" "file=$NIX_DISK_IMAGE,cache=writeback,werror=report"} \ -kernel ${config.system.build.toplevel}/kernel \ -initrd ${config.system.build.toplevel}/initrd \ -append "$(cat ${config.system.build.toplevel}/kernel-params) init=${config.system.build.toplevel}/init regInfo=${regInfo}/registration ${kernelConsole} $QEMU_KERNEL_PARAMS" \ @@ -98,7 +114,7 @@ let ${qemuGraphics} \ ${toString config.virtualisation.qemu.options} \ $QEMU_OPTS \ - $@ + "$@" ''; @@ -319,8 +335,8 @@ in networkingOptions = mkOption { default = [ - "-net nic,vlan=0,model=virtio" - "-net user,vlan=0\${QEMU_NET_OPTS:+,$QEMU_NET_OPTS}" + "-net nic,netdev=user.0,model=virtio" + "-netdev user,id=user.0\${QEMU_NET_OPTS:+,$QEMU_NET_OPTS}" ]; type = types.listOf types.str; description = '' @@ -337,11 +353,8 @@ in mkOption { default = "virtio"; example = "scsi"; - type = types.str; - description = '' - The interface used for the virtual hard disks - (<literal>virtio</literal> or <literal>scsi</literal>). - ''; + type = types.enum [ "virtio" "scsi" "ide" ]; + description = "The interface used for the virtual hard disks."; }; }; @@ -434,9 +447,11 @@ in virtualisation.pathsInNixDB = [ config.system.build.toplevel ]; - # FIXME: Figure out how to make this work on non-x86 - virtualisation.qemu.options = - mkIf (pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64) [ "-vga std" "-usbdevice tablet" ]; + # FIXME: Consolidate this one day. + virtualisation.qemu.options = mkMerge [ + (mkIf (pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64) [ "-vga std" "-usb" "-device usb-tablet,bus=usb-bus.0" ]) + (mkIf (pkgs.stdenv.isAarch32 || pkgs.stdenv.isAarch64) [ "-device virtio-gpu-pci" "-device usb-ehci,id=usb0" "-device usb-kbd" "-device usb-tablet" ]) + ]; # Mount the host filesystem via 9P, and bind-mount the Nix store # of the host into our own filesystem. We use mkVMOverride to diff --git a/nixos/release.nix b/nixos/release.nix index a1310a409a1e..4ad947f11d10 100644 --- a/nixos/release.nix +++ b/nixos/release.nix @@ -17,14 +17,14 @@ let } // args); # Note: only supportedSystems are considered. - callTestOnTheseSystems = systems: fn: args: - forTheseSystems + callTestOnMatchingSystems = systems: fn: args: + forMatchingSystems (intersectLists supportedSystems systems) (system: hydraJob (importTest fn args system)); - callTest = callTestOnTheseSystems supportedSystems; + callTest = callTestOnMatchingSystems supportedSystems; - callSubTests = callSubTestsOnTheseSystems supportedSystems; - callSubTestsOnTheseSystems = systems: fn: args: let + callSubTests = callSubTestsOnMatchingSystems supportedSystems; + callSubTestsOnMatchingSystems = systems: fn: args: let discover = attrs: let subTests = filterAttrs (const (hasAttr "test")) attrs; in mapAttrs (const (t: hydraJob t.test)) subTests; @@ -55,6 +55,17 @@ let }).config.system.build.isoImage); + makeSdImage = + { module, maintainers ? ["dezgeg"], system }: + + with import nixpkgs { inherit system; }; + + hydraJob ((import lib/eval-config.nix { + inherit system; + modules = [ module versionModule ]; + }).config.system.build.sdImage); + + makeSystemTarball = { module, maintainers ? ["viric"], system }: @@ -113,7 +124,6 @@ let preferLocalBuild = true; }; - in rec { channel = import lib/make-channel.nix { inherit pkgs nixpkgs version versionSuffix; }; @@ -121,13 +131,14 @@ in rec { manual = buildFromConfig ({ pkgs, ... }: { }) (config: config.system.build.manual.manual); manualEpub = (buildFromConfig ({ pkgs, ... }: { }) (config: config.system.build.manual.manualEpub)); manpages = buildFromConfig ({ pkgs, ... }: { }) (config: config.system.build.manual.manpages); + manualGeneratedSources = buildFromConfig ({ pkgs, ... }: { }) (config: config.system.build.manual.generatedSources); options = (buildFromConfig ({ pkgs, ... }: { }) (config: config.system.build.manual.optionsJSON)).x86_64-linux; # Build the initial ramdisk so Hydra can keep track of its size over time. initialRamdisk = buildFromConfig ({ pkgs, ... }: { }) (config: config.system.build.initialRamdisk); - netboot = forTheseSystems [ "x86_64-linux" "aarch64-linux" ] (system: makeNetboot { + netboot = forMatchingSystems [ "x86_64-linux" "aarch64-linux" ] (system: makeNetboot { inherit system; modules = [ ./modules/installer/netboot/netboot-minimal.nix @@ -141,7 +152,7 @@ in rec { inherit system; }); - iso_graphical = forTheseSystems [ "x86_64-linux" ] (system: makeIso { + iso_graphical = forMatchingSystems [ "x86_64-linux" ] (system: makeIso { module = ./modules/installer/cd-dvd/installation-cd-graphical-kde.nix; type = "graphical"; inherit system; @@ -149,15 +160,23 @@ in rec { # A variant with a more recent (but possibly less stable) kernel # that might support more hardware. - iso_minimal_new_kernel = forTheseSystems [ "x86_64-linux" ] (system: makeIso { + iso_minimal_new_kernel = forMatchingSystems [ "x86_64-linux" ] (system: makeIso { module = ./modules/installer/cd-dvd/installation-cd-minimal-new-kernel.nix; type = "minimal-new-kernel"; inherit system; }); + sd_image = forMatchingSystems [ "armv6l-linux" "armv7l-linux" "aarch64-linux" ] (system: makeSdImage { + module = { + armv6l-linux = ./modules/installer/cd-dvd/sd-image-raspberrypi.nix; + armv7l-linux = ./modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix; + aarch64-linux = ./modules/installer/cd-dvd/sd-image-aarch64.nix; + }.${system}; + inherit system; + }); # A bootable VirtualBox virtual appliance as an OVA file (i.e. packaged OVF). - ova = forTheseSystems [ "x86_64-linux" ] (system: + ova = forMatchingSystems [ "x86_64-linux" ] (system: with import nixpkgs { inherit system; }; @@ -233,9 +252,9 @@ in rec { tests.boot-stage1 = callTest tests/boot-stage1.nix {}; tests.borgbackup = callTest tests/borgbackup.nix {}; tests.buildbot = callTest tests/buildbot.nix {}; - tests.cadvisor = callTestOnTheseSystems ["x86_64-linux"] tests/cadvisor.nix {}; - tests.ceph = callTestOnTheseSystems ["x86_64-linux"] tests/ceph.nix {}; - tests.chromium = (callSubTestsOnTheseSystems ["x86_64-linux"] tests/chromium.nix {}).stable or {}; + tests.cadvisor = callTestOnMatchingSystems ["x86_64-linux"] tests/cadvisor.nix {}; + tests.ceph = callTestOnMatchingSystems ["x86_64-linux"] tests/ceph.nix {}; + tests.chromium = (callSubTestsOnMatchingSystems ["x86_64-linux"] tests/chromium.nix {}).stable or {}; tests.cjdns = callTest tests/cjdns.nix {}; tests.cloud-init = callTest tests/cloud-init.nix {}; tests.containers-ipv4 = callTest tests/containers-ipv4.nix {}; @@ -249,21 +268,23 @@ in rec { tests.containers-hosts = callTest tests/containers-hosts.nix {}; tests.containers-macvlans = callTest tests/containers-macvlans.nix {}; tests.couchdb = callTest tests/couchdb.nix {}; - tests.docker = callTestOnTheseSystems ["x86_64-linux"] tests/docker.nix {}; - tests.docker-tools = callTestOnTheseSystems ["x86_64-linux"] tests/docker-tools.nix {}; - tests.docker-edge = callTestOnTheseSystems ["x86_64-linux"] tests/docker-edge.nix {}; + tests.deluge = callTest tests/deluge.nix {}; + tests.docker = callTestOnMatchingSystems ["x86_64-linux"] tests/docker.nix {}; + tests.docker-tools = callTestOnMatchingSystems ["x86_64-linux"] tests/docker-tools.nix {}; + tests.docker-tools-overlay = callTestOnMatchingSystems ["x86_64-linux"] tests/docker-tools-overlay.nix {}; + tests.docker-edge = callTestOnMatchingSystems ["x86_64-linux"] tests/docker-edge.nix {}; tests.dovecot = callTest tests/dovecot.nix {}; - tests.dnscrypt-proxy = callTestOnTheseSystems ["x86_64-linux"] tests/dnscrypt-proxy.nix {}; + tests.dnscrypt-proxy = callTestOnMatchingSystems ["x86_64-linux"] tests/dnscrypt-proxy.nix {}; tests.ecryptfs = callTest tests/ecryptfs.nix {}; - tests.etcd = callTestOnTheseSystems ["x86_64-linux"] tests/etcd.nix {}; - tests.ec2-nixops = (callSubTestsOnTheseSystems ["x86_64-linux"] tests/ec2.nix {}).boot-ec2-nixops or {}; - tests.ec2-config = (callSubTestsOnTheseSystems ["x86_64-linux"] tests/ec2.nix {}).boot-ec2-config or {}; - tests.elk = callSubTestsOnTheseSystems ["x86_64-linux"] tests/elk.nix {}; + tests.etcd = callTestOnMatchingSystems ["x86_64-linux"] tests/etcd.nix {}; + tests.ec2-nixops = (callSubTestsOnMatchingSystems ["x86_64-linux"] tests/ec2.nix {}).boot-ec2-nixops or {}; + tests.ec2-config = (callSubTestsOnMatchingSystems ["x86_64-linux"] tests/ec2.nix {}).boot-ec2-config or {}; + tests.elk = callSubTestsOnMatchingSystems ["x86_64-linux"] tests/elk.nix {}; tests.env = callTest tests/env.nix {}; tests.ferm = callTest tests/ferm.nix {}; tests.firefox = callTest tests/firefox.nix {}; tests.firewall = callTest tests/firewall.nix {}; - tests.fleet = callTestOnTheseSystems ["x86_64-linux"] tests/fleet.nix {}; + tests.fleet = callTestOnMatchingSystems ["x86_64-linux"] tests/fleet.nix {}; tests.fwupd = callTest tests/fwupd.nix {}; #tests.gitlab = callTest tests/gitlab.nix {}; tests.gitolite = callTest tests/gitolite.nix {}; @@ -280,11 +301,13 @@ in rec { tests.hound = callTest tests/hound.nix {}; tests.hocker-fetchdocker = callTest tests/hocker-fetchdocker {}; tests.i3wm = callTest tests/i3wm.nix {}; + tests.iftop = callTest tests/iftop.nix {}; tests.initrd-network-ssh = callTest tests/initrd-network-ssh {}; tests.installer = callSubTests tests/installer.nix {}; tests.influxdb = callTest tests/influxdb.nix {}; tests.ipv6 = callTest tests/ipv6.nix {}; tests.jenkins = callTest tests/jenkins.nix {}; + tests.osquery = callTest tests/osquery.nix {}; tests.plasma5 = callTest tests/plasma5.nix {}; tests.plotinus = callTest tests/plotinus.nix {}; tests.keymap = callSubTests tests/keymap.nix {}; @@ -296,7 +319,10 @@ in rec { tests.kernel-copperhead = callTest tests/kernel-copperhead.nix {}; tests.kernel-latest = callTest tests/kernel-latest.nix {}; tests.kernel-lts = callTest tests/kernel-lts.nix {}; - tests.kubernetes = callSubTestsOnTheseSystems ["x86_64-linux"] tests/kubernetes/default.nix {}; + tests.kubernetes.dns = callSubTestsOnMatchingSystems ["x86_64-linux"] tests/kubernetes/dns.nix {}; + ## kubernetes.e2e should eventually replace kubernetes.rbac when it works + #tests.kubernetes.e2e = callSubTestsOnMatchingSystems ["x86_64-linux"] tests/kubernetes/e2e.nix {}; + tests.kubernetes.rbac = callSubTestsOnMatchingSystems ["x86_64-linux"] tests/kubernetes/rbac.nix {}; tests.latestKernel.login = callTest tests/login.nix { latestKernel = true; }; tests.ldap = callTest tests/ldap.nix {}; #tests.lightdm = callTest tests/lightdm.nix {}; @@ -326,14 +352,14 @@ in rec { tests.nginx = callTest tests/nginx.nix { }; tests.nghttpx = callTest tests/nghttpx.nix { }; tests.nix-ssh-serve = callTest tests/nix-ssh-serve.nix { }; - tests.novacomd = callTestOnTheseSystems ["x86_64-linux"] tests/novacomd.nix { }; + tests.novacomd = callTestOnMatchingSystems ["x86_64-linux"] tests/novacomd.nix { }; tests.leaps = callTest tests/leaps.nix { }; tests.nsd = callTest tests/nsd.nix {}; tests.openssh = callTest tests/openssh.nix {}; tests.openldap = callTest tests/openldap.nix {}; tests.owncloud = callTest tests/owncloud.nix {}; tests.pam-oath-login = callTest tests/pam-oath-login.nix {}; - #tests.panamax = callTestOnTheseSystems ["x86_64-linux"] tests/panamax.nix {}; + #tests.panamax = callTestOnMatchingSystems ["x86_64-linux"] tests/panamax.nix {}; tests.peerflix = callTest tests/peerflix.nix {}; tests.php-pcre = callTest tests/php-pcre.nix {}; tests.postgresql = callSubTests tests/postgresql.nix {}; @@ -344,8 +370,9 @@ 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.prosody = callTest tests/prosody.nix {}; tests.proxy = callTest tests/proxy.nix {}; - # tests.quagga = callTest tests/quagga.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 {}; @@ -359,15 +386,18 @@ in rec { tests.smokeping = callTest tests/smokeping.nix {}; tests.snapper = callTest tests/snapper.nix {}; tests.statsd = callTest tests/statsd.nix {}; + tests.strongswan-swanctl = callTest tests/strongswan-swanctl.nix {}; tests.sudo = callTest tests/sudo.nix {}; tests.systemd = callTest tests/systemd.nix {}; tests.switchTest = callTest tests/switch-test.nix {}; tests.taskserver = callTest tests/taskserver.nix {}; tests.tomcat = callTest tests/tomcat.nix {}; + tests.transmission = callTest tests/transmission.nix {}; tests.udisks2 = callTest tests/udisks2.nix {}; tests.vault = callTest tests/vault.nix {}; - tests.virtualbox = callSubTestsOnTheseSystems ["x86_64-linux"] tests/virtualbox.nix {}; + tests.virtualbox = callSubTestsOnMatchingSystems ["x86_64-linux"] tests/virtualbox.nix {}; tests.wordpress = callTest tests/wordpress.nix {}; + tests.xautolock = callTest tests/xautolock.nix {}; tests.xfce = callTest tests/xfce.nix {}; tests.xmonad = callTest tests/xmonad.nix {}; tests.xrdp = callTest tests/xrdp.nix {}; diff --git a/nixos/tests/atd.nix b/nixos/tests/atd.nix index c2c0a716e0de..5260c8ddfb82 100644 --- a/nixos/tests/atd.nix +++ b/nixos/tests/atd.nix @@ -17,20 +17,14 @@ import ./make-test.nix ({ pkgs, lib, ... }: startAll; $machine->fail("test -f ~root/at-1"); - $machine->fail("test -f ~root/batch-1"); $machine->fail("test -f ~alice/at-1"); - $machine->fail("test -f ~alice/batch-1"); $machine->succeed("echo 'touch ~root/at-1' | at now+1min"); - $machine->succeed("echo 'touch ~root/batch-1' | batch"); $machine->succeed("su - alice -c \"echo 'touch at-1' | at now+1min\""); - $machine->succeed("su - alice -c \"echo 'touch batch-1' | batch\""); $machine->succeed("sleep 1.5m"); $machine->succeed("test -f ~root/at-1"); - $machine->succeed("test -f ~root/batch-1"); $machine->succeed("test -f ~alice/at-1"); - $machine->succeed("test -f ~alice/batch-1"); ''; }) diff --git a/nixos/tests/borgbackup.nix b/nixos/tests/borgbackup.nix index 123b02be7251..36731773de27 100644 --- a/nixos/tests/borgbackup.nix +++ b/nixos/tests/borgbackup.nix @@ -1,21 +1,162 @@ -import ./make-test.nix ({ pkgs, ...}: { +import ./make-test.nix ({ pkgs, ... }: + +let + passphrase = "supersecret"; + dataDir = "/ran:dom/data"; + excludeFile = "not_this_file"; + keepFile = "important_file"; + keepFileData = "important_data"; + localRepo = "/root/back:up"; + archiveName = "my_archive"; + remoteRepo = "borg@server:."; # No need to specify path + privateKey = pkgs.writeText "id_ed25519" '' + -----BEGIN OPENSSH PRIVATE KEY----- + b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW + QyNTUxOQAAACBx8UB04Q6Q/fwDFjakHq904PYFzG9pU2TJ9KXpaPMcrwAAAJB+cF5HfnBe + RwAAAAtzc2gtZWQyNTUxOQAAACBx8UB04Q6Q/fwDFjakHq904PYFzG9pU2TJ9KXpaPMcrw + AAAEBN75NsJZSpt63faCuaD75Unko0JjlSDxMhYHAPJk2/xXHxQHThDpD9/AMWNqQer3Tg + 9gXMb2lTZMn0pelo8xyvAAAADXJzY2h1ZXR6QGt1cnQ= + -----END OPENSSH PRIVATE KEY----- + ''; + publicKey = '' + ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHHxQHThDpD9/AMWNqQer3Tg9gXMb2lTZMn0pelo8xyv root@client + ''; + privateKeyAppendOnly = pkgs.writeText "id_ed25519" '' + -----BEGIN OPENSSH PRIVATE KEY----- + b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW + QyNTUxOQAAACBacZuz1ELGQdhI7PF6dGFafCDlvh8pSEc4cHjkW0QjLwAAAJC9YTxxvWE8 + cQAAAAtzc2gtZWQyNTUxOQAAACBacZuz1ELGQdhI7PF6dGFafCDlvh8pSEc4cHjkW0QjLw + AAAEAAhV7wTl5dL/lz+PF/d4PnZXuG1Id6L/mFEiGT1tZsuFpxm7PUQsZB2Ejs8Xp0YVp8 + IOW+HylIRzhweORbRCMvAAAADXJzY2h1ZXR6QGt1cnQ= + -----END OPENSSH PRIVATE KEY----- + ''; + publicKeyAppendOnly = '' + ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFpxm7PUQsZB2Ejs8Xp0YVp8IOW+HylIRzhweORbRCMv root@client + ''; + +in { name = "borgbackup"; - meta = with pkgs.stdenv.lib.maintainers; { - maintainers = [ mic92 ]; + meta = with pkgs.stdenv.lib; { + maintainers = with maintainers; [ dotlambda ]; }; nodes = { - machine = { config, pkgs, ... }: { - environment.systemPackages = [ pkgs.borgbackup ]; + client = { config, pkgs, ... }: { + services.borgbackup.jobs = { + + local = rec { + paths = dataDir; + repo = localRepo; + preHook = '' + # Don't append a timestamp + archiveName="${archiveName}" + ''; + encryption = { + mode = "repokey"; + inherit passphrase; + }; + compression = "auto,zlib,9"; + prune.keep = { + within = "1y"; + yearly = 5; + }; + exclude = [ "*/${excludeFile}" ]; + postHook = "echo post"; + startAt = [ ]; # Do not run automatically + }; + + remote = { + paths = dataDir; + repo = remoteRepo; + encryption.mode = "none"; + startAt = [ ]; + environment.BORG_RSH = "ssh -oStrictHostKeyChecking=no -i /root/id_ed25519"; + }; + + remoteAppendOnly = { + paths = dataDir; + repo = remoteRepo; + encryption.mode = "none"; + startAt = [ ]; + environment.BORG_RSH = "ssh -oStrictHostKeyChecking=no -i /root/id_ed25519.appendOnly"; + }; + + }; + }; + + server = { config, pkgs, ... }: { + services.openssh = { + enable = true; + passwordAuthentication = false; + challengeResponseAuthentication = false; + }; + + services.borgbackup.repos.repo1 = { + authorizedKeys = [ publicKey ]; + path = "/data/borgbackup"; + }; + + # Second repo to make sure the authorizedKeys options are merged correctly + services.borgbackup.repos.repo2 = { + authorizedKeysAppendOnly = [ publicKeyAppendOnly ]; + path = "/data/borgbackup"; + quota = ".5G"; + }; }; }; testScript = '' - my $borg = "BORG_PASSPHRASE=supersecret borg"; - $machine->succeed("$borg init --encryption=repokey /tmp/backup"); - $machine->succeed("mkdir /tmp/data/ && echo 'data' >/tmp/data/file"); - $machine->succeed("$borg create --stats /tmp/backup::test /tmp/data"); - $machine->succeed("$borg extract /tmp/backup::test"); - $machine->succeed('c=$(cat data/file) && echo "c = $c" >&2 && [[ "$c" == "data" ]]'); + startAll; + + $client->fail('test -d "${remoteRepo}"'); + + $client->succeed("cp ${privateKey} /root/id_ed25519"); + $client->succeed("chmod 0600 /root/id_ed25519"); + $client->succeed("cp ${privateKeyAppendOnly} /root/id_ed25519.appendOnly"); + $client->succeed("chmod 0600 /root/id_ed25519.appendOnly"); + + $client->succeed("mkdir -p ${dataDir}"); + $client->succeed("touch ${dataDir}/${excludeFile}"); + $client->succeed("echo '${keepFileData}' > ${dataDir}/${keepFile}"); + + subtest "local", sub { + my $borg = "BORG_PASSPHRASE='${passphrase}' borg"; + $client->systemctl("start --wait borgbackup-job-local"); + $client->fail("systemctl is-failed borgbackup-job-local"); + # Make sure exactly one archive has been created + $client->succeed("c=\$($borg list '${localRepo}' | wc -l) && [[ \$c == '1' ]]"); + # Make sure excludeFile has been excluded + $client->fail("$borg list '${localRepo}::${archiveName}' | grep -qF '${excludeFile}'"); + # Make sure keepFile has the correct content + $client->succeed("$borg extract '${localRepo}::${archiveName}'"); + $client->succeed('c=$(cat ${dataDir}/${keepFile}) && [[ "$c" == "${keepFileData}" ]]'); + }; + + subtest "remote", sub { + my $borg = "BORG_RSH='ssh -oStrictHostKeyChecking=no -i /root/id_ed25519' borg"; + $server->waitForUnit("sshd.service"); + $client->waitForUnit("network.target"); + $client->systemctl("start --wait borgbackup-job-remote"); + $client->fail("systemctl is-failed borgbackup-job-remote"); + + # Make sure we can't access repos other than the specified one + $client->fail("$borg list borg\@server:wrong"); + + #TODO: Make sure that data is actually deleted + }; + + subtest "remoteAppendOnly", sub { + my $borg = "BORG_RSH='ssh -oStrictHostKeyChecking=no -i /root/id_ed25519.appendOnly' borg"; + $server->waitForUnit("sshd.service"); + $client->waitForUnit("network.target"); + $client->systemctl("start --wait borgbackup-job-remoteAppendOnly"); + $client->fail("systemctl is-failed borgbackup-job-remoteAppendOnly"); + + # Make sure we can't access repos other than the specified one + $client->fail("$borg list borg\@server:wrong"); + + #TODO: Make sure that data is not actually deleted + }; + ''; }) diff --git a/nixos/tests/chromium.nix b/nixos/tests/chromium.nix index 3a2c65164766..c341e83961a8 100644 --- a/nixos/tests/chromium.nix +++ b/nixos/tests/chromium.nix @@ -94,6 +94,11 @@ mapAttrs (channel: chromiumPkg: makeTest rec { ''}"); if ($status == 0) { $ret = 1; + + # XXX: Somehow Chromium is not accepting keystrokes for a few + # seconds after a new window has appeared, so let's wait a while. + $machine->sleep(10); + last; } $machine->sleep(1); @@ -151,11 +156,11 @@ mapAttrs (channel: chromiumPkg: makeTest rec { $machine->screenshot("sandbox_info"); - $machine->succeed(ru "${xdo "submit-url" '' + $machine->succeed(ru "${xdo "find-window" '' search --sync --onlyvisible --name "sandbox status" windowfocus --sync ''}"); - $machine->succeed(ru "${xdo "submit-url" '' + $machine->succeed(ru "${xdo "copy-sandbox-info" '' key --delay 1000 Ctrl+a Ctrl+c ''}"); @@ -166,6 +171,26 @@ mapAttrs (channel: chromiumPkg: makeTest rec { && $clipboard =~ /network namespaces.*yes/mi && $clipboard =~ /seccomp.*sandbox.*yes/mi && $clipboard =~ /you are adequately sandboxed/mi; + + $machine->sleep(1); + $machine->succeed(ru "${xdo "find-window-after-copy" '' + search --onlyvisible --name "sandbox status" + ''}"); + + my $clipboard = $machine->succeed(ru "echo void | ${pkgs.xclip}/bin/xclip -i"); + $machine->succeed(ru "${xdo "copy-sandbox-info" '' + key --delay 1000 Ctrl+a Ctrl+c + ''}"); + + my $clipboard = $machine->succeed(ru "${pkgs.xclip}/bin/xclip -o"); + die "copying twice in a row does not work properly: $clipboard" + unless $clipboard =~ /namespace sandbox.*yes/mi + && $clipboard =~ /pid namespaces.*yes/mi + && $clipboard =~ /network namespaces.*yes/mi + && $clipboard =~ /seccomp.*sandbox.*yes/mi + && $clipboard =~ /you are adequately sandboxed/mi; + + $machine->screenshot("afer_copy_from_chromium"); }; $machine->shutdown; diff --git a/nixos/tests/common/letsencrypt.nix b/nixos/tests/common/letsencrypt.nix index 9b53d9d61a16..10cde45d18a8 100644 --- a/nixos/tests/common/letsencrypt.nix +++ b/nixos/tests/common/letsencrypt.nix @@ -138,8 +138,8 @@ let boulder = let owner = "letsencrypt"; repo = "boulder"; - rev = "9866abab8962a591f06db457a4b84c518cc88243"; - version = "20170510"; + rev = "9c6a1f2adc4c26d925588f5ae366cfd4efb7813a"; + version = "20180129"; in pkgs.buildGoPackage rec { name = "${repo}-${version}"; @@ -147,7 +147,7 @@ let src = pkgs.fetchFromGitHub { name = "${name}-src"; inherit rev owner repo; - sha256 = "170m5cjngbrm36wi7wschqw8jzs7kxpcyzmshq3pcrmcpigrhna1"; + sha256 = "09kszswrifm9rc6idfaq0p1mz5w21as2qbc8gd5pphrq9cf9pn55"; }; postPatch = '' @@ -168,6 +168,18 @@ let cat "${snakeOilCa}/ca.pem" > test/test-ca.pem ''; + # Until vendored pkcs11 is go 1.9 compatible + preBuild = '' + rm -r go/src/github.com/letsencrypt/boulder/vendor/github.com/miekg/pkcs11 + ''; + + extraSrcs = map mkGoDep [ + { goPackagePath = "github.com/miekg/pkcs11"; + rev = "6dbd569b952ec150d1425722dbbe80f2c6193f83"; + sha256 = "1m8g6fx7df6hf6q6zsbyw1icjmm52dmsx28rgb0h930wagvngfwb"; + } + ]; + goPackagePath = "github.com/${owner}/${repo}"; buildInputs = [ pkgs.libtool ]; }; @@ -284,7 +296,11 @@ let ocsp-updater.after = [ "boulder-publisher" ]; ocsp-responder.args = "--config ${cfgDir}/ocsp-responder.json"; ct-test-srv = {}; - mail-test-srv.args = "--closeFirst 5"; + mail-test-srv.args = let + key = "${boulderSource}/test/mail-test-srv/minica-key.pem"; + crt = "${boulderSource}/test/mail-test-srv/minica.pem"; + in + "--closeFirst 5 --cert ${crt} --key ${key}"; }; commonPath = [ softhsm pkgs.mariadb goose boulder ]; diff --git a/nixos/tests/containers-imperative.nix b/nixos/tests/containers-imperative.nix index 7e2a54976387..015b79b1cee6 100644 --- a/nixos/tests/containers-imperative.nix +++ b/nixos/tests/containers-imperative.nix @@ -15,12 +15,12 @@ import ./make-test.nix ({ pkgs, ...} : { # container available within the VM, because we don't have network access. virtualisation.pathsInNixDB = let emptyContainer = import ../lib/eval-config.nix { - inherit (config.nixpkgs) system; + inherit (config.nixpkgs.localSystem) system; modules = lib.singleton { containers.foo.config = {}; }; }; - in [ pkgs.stdenv emptyContainer.config.containers.foo.path ]; + in [ pkgs.stdenv emptyContainer.config.containers.foo.path pkgs.libxslt ]; }; testScript = diff --git a/nixos/tests/containers-physical_interfaces.nix b/nixos/tests/containers-physical_interfaces.nix index bd1228b8e37d..bde8e175f953 100644 --- a/nixos/tests/containers-physical_interfaces.nix +++ b/nixos/tests/containers-physical_interfaces.nix @@ -52,7 +52,7 @@ import ./make-test.nix ({ pkgs, ...} : { config = { networking.bonds.bond0 = { interfaces = [ "eth1" ]; - mode = "active-backup"; + driverOptions.mode = "active-backup"; }; networking.interfaces.bond0.ipv4.addresses = [ { address = "10.10.0.3"; prefixLength = 24; } @@ -73,7 +73,7 @@ import ./make-test.nix ({ pkgs, ...} : { config = { networking.bonds.bond0 = { interfaces = [ "eth1" ]; - mode = "active-backup"; + driverOptions.mode = "active-backup"; }; networking.bridges.br0.interfaces = [ "bond0" ]; networking.interfaces.br0.ipv4.addresses = [ diff --git a/nixos/tests/containers-tmpfs.nix b/nixos/tests/containers-tmpfs.nix index 564831fa2737..873dd364369f 100644 --- a/nixos/tests/containers-tmpfs.nix +++ b/nixos/tests/containers-tmpfs.nix @@ -1,7 +1,7 @@ # Test for NixOS' container support. import ./make-test.nix ({ pkgs, ...} : { - name = "containers-bridge"; + name = "containers-tmpfs"; meta = with pkgs.stdenv.lib.maintainers; { maintainers = [ ckampka ]; }; diff --git a/nixos/tests/deluge.nix b/nixos/tests/deluge.nix new file mode 100644 index 000000000000..6119fd58447c --- /dev/null +++ b/nixos/tests/deluge.nix @@ -0,0 +1,29 @@ +import ./make-test.nix ({ pkgs, ...} : { + name = "deluge"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ flokli ]; + }; + + nodes = { + server = + { pkgs, config, ... }: + + { services.deluge = { + enable = true; + web.enable = true; + }; + networking.firewall.allowedTCPPorts = [ 8112 ]; + }; + + client = { }; + }; + + testScript = '' + startAll; + + $server->waitForUnit("deluged"); + $server->waitForUnit("delugeweb"); + $client->waitForUnit("network.target"); + $client->waitUntilSucceeds("curl --fail http://server:8112"); + ''; +}) diff --git a/nixos/tests/docker-tools-overlay.nix b/nixos/tests/docker-tools-overlay.nix new file mode 100644 index 000000000000..9d7fa3e7a8c5 --- /dev/null +++ b/nixos/tests/docker-tools-overlay.nix @@ -0,0 +1,32 @@ +# this test creates a simple GNU image with docker tools and sees if it executes + +import ./make-test.nix ({ pkgs, ... }: +{ + name = "docker-tools-overlay"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ lnl7 ]; + }; + + nodes = { + docker = + { config, pkgs, ... }: + { + virtualisation.docker.enable = true; + virtualisation.docker.storageDriver = "overlay"; # defaults to overlay2 + }; + }; + + testScript = + '' + $docker->waitForUnit("sockets.target"); + + $docker->succeed("docker load --input='${pkgs.dockerTools.examples.bash}'"); + $docker->succeed("docker run --rm ${pkgs.dockerTools.examples.bash.imageName} bash --version"); + + # Check if the nix store has correct user permissions depending on what + # storage driver is used, incorrectly built images can show up as readonly. + # drw------- 3 0 0 3 Apr 14 11:36 /nix + # drw------- 99 0 0 100 Apr 14 11:36 /nix/store + $docker->succeed("docker run --rm -u 1000:1000 ${pkgs.dockerTools.examples.bash.imageName} bash --version"); + ''; +}) diff --git a/nixos/tests/docker-tools.nix b/nixos/tests/docker-tools.nix index e52a4c3f884e..4466081d01e9 100644 --- a/nixos/tests/docker-tools.nix +++ b/nixos/tests/docker-tools.nix @@ -3,14 +3,14 @@ import ./make-test.nix ({ pkgs, ... }: { name = "docker-tools"; meta = with pkgs.stdenv.lib.maintainers; { - maintainers = [ ]; + maintainers = [ lnl7 ]; }; nodes = { docker = { config, pkgs, ... }: { virtualisation = { - diskSize = 1024; + diskSize = 2048; docker.enable = true; }; }; @@ -21,19 +21,29 @@ import ./make-test.nix ({ pkgs, ... }: { $docker->waitForUnit("sockets.target"); $docker->succeed("docker load --input='${pkgs.dockerTools.examples.bash}'"); - $docker->succeed("docker run ${pkgs.dockerTools.examples.bash.imageName} /bin/bash --version"); + $docker->succeed("docker run --rm ${pkgs.dockerTools.examples.bash.imageName} bash --version"); + $docker->succeed("docker rmi ${pkgs.dockerTools.examples.bash.imageName}"); + # Check if the nix store is correctly initialized by listing dependencies of the installed Nix binary $docker->succeed("docker load --input='${pkgs.dockerTools.examples.nix}'"); - $docker->succeed("docker run ${pkgs.dockerTools.examples.nix.imageName} /bin/nix-store -qR ${pkgs.nix}"); + $docker->succeed("docker run --rm ${pkgs.dockerTools.examples.nix.imageName} nix-store -qR ${pkgs.nix}"); + $docker->succeed("docker rmi ${pkgs.dockerTools.examples.nix.imageName}"); # To test the pullImage tool $docker->succeed("docker load --input='${pkgs.dockerTools.examples.nixFromDockerHub}'"); - $docker->succeed("docker run nixos/nix:1.11 nix-store --version"); + $docker->succeed("docker run --rm nixos/nix:1.11 nix-store --version"); + $docker->succeed("docker rmi nixos/nix:1.11"); # To test runAsRoot and entry point $docker->succeed("docker load --input='${pkgs.dockerTools.examples.nginx}'"); $docker->succeed("docker run --name nginx -d -p 8000:80 ${pkgs.dockerTools.examples.nginx.imageName}"); $docker->waitUntilSucceeds('curl http://localhost:8000/'); $docker->succeed("docker rm --force nginx"); + $docker->succeed("docker rmi '${pkgs.dockerTools.examples.nginx.imageName}'"); + + # An pulled image can be used as base image + $docker->succeed("docker load --input='${pkgs.dockerTools.examples.onTopOfPulledImage}'"); + $docker->succeed("docker run --rm ontopofpulledimage hello"); + $docker->succeed("docker rmi ontopofpulledimage"); ''; }) diff --git a/nixos/tests/dovecot.nix b/nixos/tests/dovecot.nix index 3814855ed8e7..156079d1d585 100644 --- a/nixos/tests/dovecot.nix +++ b/nixos/tests/dovecot.nix @@ -18,6 +18,18 @@ import ./make-test.nix { MAIL ''; + sendTestMailViaDeliveryAgent = pkgs.writeScriptBin "send-lda" '' + #!${pkgs.stdenv.shell} + + exec ${pkgs.dovecot}/libexec/dovecot/deliver -d bob <<MAIL + From: root@localhost + To: bob@localhost + Subject: Something else... + + I'm running short of ideas! + MAIL + ''; + testImap = pkgs.writeScriptBin "test-imap" '' #!${pkgs.python3.interpreter} import imaplib @@ -39,24 +51,25 @@ import ./make-test.nix { pop = poplib.POP3('localhost') try: - pop.user('alice') + pop.user('bob') pop.pass_('foobar') assert len(pop.list()[1]) == 1 status, fullmail, size = pop.retr(1) assert status.startswith(b'+OK ') body = b"".join(fullmail[fullmail.index(b""):]).strip() - assert body == b'Hello world!' + assert body == b"I'm running short of ideas!" finally: pop.quit() ''; - in [ sendTestMail testImap testPop ]; + in [ sendTestMail sendTestMailViaDeliveryAgent testImap testPop ]; }; testScript = '' $machine->waitForUnit('postfix.service'); $machine->waitForUnit('dovecot2.service'); $machine->succeed('send-testmail'); + $machine->succeed('send-lda'); $machine->waitUntilFails('[ "$(postqueue -p)" != "Mail queue is empty" ]'); $machine->succeed('test-imap'); $machine->succeed('test-pop'); diff --git a/nixos/tests/gnome3-gdm.nix b/nixos/tests/gnome3-gdm.nix index 4b459e93e1be..71ae1709d526 100644 --- a/nixos/tests/gnome3-gdm.nix +++ b/nixos/tests/gnome3-gdm.nix @@ -26,15 +26,22 @@ import ./make-test.nix ({ pkgs, ...} : { testScript = '' + # wait for gdm to start and bring up X + $machine->waitForUnit("display-manager.service"); $machine->waitForX; - $machine->sleep(15); + + # wait for alice to be logged in + $machine->waitForUnit("default.target","alice"); # Check that logging in has given the user ownership of devices. $machine->succeed("getfacl /dev/snd/timer | grep -q alice"); - $machine->succeed("su - alice -c 'DISPLAY=:0.0 gnome-terminal &'"); - $machine->succeed("xauth merge ~alice/.Xauthority"); + # open a terminal and check it's there + $machine->succeed("su - alice -c 'DISPLAY=:0.0 XAUTHORITY=/run/user/\$UID/gdm/Xauthority gnome-terminal'"); + $machine->succeed("xauth merge /run/user/1000/gdm/Xauthority"); $machine->waitForWindow(qr/Terminal/); + + # wait to get a nice screenshot $machine->sleep(20); $machine->screenshot("screen"); ''; diff --git a/nixos/tests/grafana.nix b/nixos/tests/grafana.nix index 16b8181498a6..d45776c3ee29 100644 --- a/nixos/tests/grafana.nix +++ b/nixos/tests/grafana.nix @@ -20,6 +20,6 @@ import ./make-test.nix ({ lib, ... }: $machine->start; $machine->waitForUnit("grafana.service"); $machine->waitForOpenPort(3000); - $machine->succeed("curl -sS http://127.0.0.1:3000/"); + $machine->succeed("curl -sSfL http://127.0.0.1:3000/"); ''; }) diff --git a/nixos/tests/hibernate.nix b/nixos/tests/hibernate.nix index a95235887e89..3ae2bdffed90 100644 --- a/nixos/tests/hibernate.nix +++ b/nixos/tests/hibernate.nix @@ -37,7 +37,7 @@ import ./make-test.nix (pkgs: { $machine->waitForShutdown; $machine->start; $probe->waitForUnit("network.target"); - $probe->waitUntilSucceeds("echo test | nc machine 4444 -q 0"); + $probe->waitUntilSucceeds("echo test | nc machine 4444 -N"); ''; }) diff --git a/nixos/tests/home-assistant.nix b/nixos/tests/home-assistant.nix index 2e45dc78471f..4ebccb7ab868 100644 --- a/nixos/tests/home-assistant.nix +++ b/nixos/tests/home-assistant.nix @@ -51,9 +51,9 @@ in { startAll; $hass->waitForUnit("home-assistant.service"); - # Since config is specified using a Nix attribute set, - # configuration.yaml is a link to the Nix store - $hass->succeed("test -L ${configDir}/configuration.yaml"); + # The config is specified using a Nix attribute set, + # but then converted from JSON to YAML + $hass->succeed("test -f ${configDir}/configuration.yaml"); # Check that Home Assistant's web interface and API can be reached $hass->waitForOpenPort(8123); diff --git a/nixos/tests/iftop.nix b/nixos/tests/iftop.nix new file mode 100644 index 000000000000..21ff3cafed7c --- /dev/null +++ b/nixos/tests/iftop.nix @@ -0,0 +1,30 @@ +import ./make-test.nix ({ pkgs, lib, ... }: + +with lib; + +{ + name = "iftop"; + meta.maintainers = with pkgs.stdenv.lib.maintainers; [ ma27 ]; + + nodes = { + withIftop = { + imports = [ ./common/user-account.nix ]; + + programs.iftop.enable = true; + }; + withoutIftop = { + imports = [ ./common/user-account.nix ]; + }; + }; + + testScript = '' + subtest "machine with iftop enabled", sub { + $withIftop->start; + $withIftop->succeed("su -l alice -c 'iftop -t -s 1'"); + }; + subtest "machine without iftop", sub { + $withoutIftop->start; + $withoutIftop->mustFail("su -l alice -c 'iftop -t -s 1'"); + }; + ''; +}) diff --git a/nixos/tests/installer.nix b/nixos/tests/installer.nix index c12919540a30..acf248d0a5a6 100644 --- a/nixos/tests/installer.nix +++ b/nixos/tests/installer.nix @@ -69,13 +69,20 @@ let let iface = if grubVersion == 1 then "ide" else "virtio"; isEfi = bootLoader == "systemd-boot" || (bootLoader == "grub" && grubUseEfi); + + # FIXME don't duplicate the -enable-kvm etc. flags here yet again! qemuFlags = (if system == "x86_64-linux" then "-m 768 " else "-m 512 ") + - (optionalString (system == "x86_64-linux") "-cpu kvm64 "); + (optionalString (system == "x86_64-linux") "-cpu kvm64 ") + + (optionalString (system == "aarch64-linux") "-enable-kvm -machine virt,gic-version=host -cpu host "); + hdFlags = ''hda => "vm-state-machine/machine.qcow2", hdaInterface => "${iface}", '' - + optionalString isEfi ''bios => "${pkgs.OVMF.fd}/FV/OVMF.fd", ''; - in - '' + + optionalString isEfi (if pkgs.stdenv.isAarch64 + then ''bios => "${pkgs.OVMF.fd}/FV/QEMU_EFI.fd", '' + else ''bios => "${pkgs.OVMF.fd}/FV/OVMF.fd", ''); + in if !isEfi && !(pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64) then + throw "Non-EFI boot methods are only supported on i686 / x86_64" + else '' $machine->start; # Make sure that we get a login prompt etc. diff --git a/nixos/tests/kernel-copperhead.nix b/nixos/tests/kernel-copperhead.nix index 07427d7f2a89..aa133c9b0aa7 100644 --- a/nixos/tests/kernel-copperhead.nix +++ b/nixos/tests/kernel-copperhead.nix @@ -6,14 +6,14 @@ import ./make-test.nix ({ pkgs, ...} : { machine = { config, lib, pkgs, ... }: { - boot.kernelPackages = pkgs.linuxPackages_hardened_copperhead; + boot.kernelPackages = pkgs.linuxPackages_copperhead_lts; }; testScript = '' $machine->succeed("uname -a"); $machine->succeed("uname -s | grep 'Linux'"); - $machine->succeed("uname -a | grep '${pkgs.linuxPackages_hardened_copperhead.kernel.modDirVersion}'"); + $machine->succeed("uname -a | grep '${pkgs.linuxPackages_copperhead_lts.kernel.modDirVersion}'"); $machine->succeed("uname -a | grep 'hardened'"); ''; }) diff --git a/nixos/tests/keymap.nix b/nixos/tests/keymap.nix index caa5f7107c23..be880388314c 100644 --- a/nixos/tests/keymap.nix +++ b/nixos/tests/keymap.nix @@ -3,46 +3,36 @@ with import ../lib/testing.nix { inherit system; }; let + readyFile = "/tmp/readerReady"; + resultFile = "/tmp/readerResult"; + testReader = pkgs.writeScript "test-input-reader" '' #!${pkgs.stdenv.shell} - readInput() { - touch /tmp/reader.ready - echo "Waiting for '$1' to be typed" - read -r -n1 c - if [ "$c" = "$2" ]; then - echo "SUCCESS: Got back '$c' as expected." - echo 0 >&2 - else - echo "FAIL: Expected '$2' but got '$c' instead." - echo 1 >&2 - fi - } - - main() { - error=0 - while [ $# -gt 0 ]; do - ret="$((readInput "$2" "$3" | systemd-cat -t "$1") 2>&1)" - if [ $ret -ne 0 ]; then error=1; fi - shift 3 - done - return $error - } - - main "$@"; echo -n $? > /tmp/reader.exit + rm -f ${resultFile} ${resultFile}.tmp + logger "testReader: START: Waiting for $1 characters, expecting '$2'." + touch ${readyFile} + read -r -N $1 chars + rm -f ${readyFile} + + if [ "$chars" == "$2" ]; then + logger -s "testReader: PASS: Got '$2' as expected." 2>${resultFile}.tmp + else + logger -s "testReader: FAIL: Expected '$2' but got '$chars'." 2>${resultFile}.tmp + fi + # rename after the file is written to prevent a race condition + mv ${resultFile}.tmp ${resultFile} ''; - mkReaderInput = testname: { qwerty, expect }: with pkgs.lib; let - lq = length qwerty; - le = length expect; - msg = "`qwerty' (${lq}) and `expect' (${le}) lists" - + " need to be of the same length!"; - result = flatten (zipListsWith (a: b: [testname a b]) qwerty expect); - in if lq != le then throw msg else result; mkKeyboardTest = layout: { extraConfig ? {}, tests }: with pkgs.lib; let - readerInput = flatten (mapAttrsToList mkReaderInput tests); + combinedTests = foldAttrs (acc: val: acc ++ val) [] (builtins.attrValues tests); perlStr = val: "'${escape ["'" "\\"] val}'"; - perlReaderInput = concatMapStringsSep ", " perlStr readerInput; + lq = length combinedTests.qwerty; + le = length combinedTests.expect; + msg = "length mismatch between qwerty (${toString lq}) and expect (${toString le}) lists!"; + send = concatMapStringsSep ", " perlStr combinedTests.qwerty; + expect = if (lq == le) then concatStrings combinedTests.expect else throw msg; + in makeTest { name = "keymap-${layout}"; @@ -50,38 +40,40 @@ let machine.i18n.consoleKeyMap = mkOverride 900 layout; machine.services.xserver.layout = mkOverride 900 layout; machine.imports = [ ./common/x11.nix extraConfig ]; - machine.services.xserver.displayManager.slim.enable = true; testScript = '' - sub waitCatAndDelete ($) { - return $machine->succeed( - "for i in \$(seq 600); do if [ -e '$_[0]' ]; then ". - "cat '$_[0]' && rm -f '$_[0]' && exit 0; ". - "fi; sleep 0.1; done; echo timed out after 60 seconds >&2; exit 1" - ); - }; sub mkTest ($$) { my ($desc, $cmd) = @_; - my @testdata = (${perlReaderInput}); - my $shellTestdata = join ' ', map { "'".s/'/'\\'''/gr."'" } @testdata; - subtest $desc, sub { - $machine->succeed("$cmd ${testReader} $shellTestdata &"); - while (my ($testname, $qwerty, $expect) = splice(@testdata, 0, 3)) { - waitCatAndDelete "/tmp/reader.ready"; - $machine->sendKeys($qwerty); - }; - my $exitcode = waitCatAndDelete "/tmp/reader.exit"; - die "tests for $desc failed" if $exitcode ne 0; + # prepare and start testReader + $machine->execute("rm -f ${readyFile} ${resultFile}"); + $machine->succeed("$cmd ${testReader} ${toString le} ".q(${escapeShellArg expect} & )); + + if ($desc eq "Xorg keymap") { + # make sure the xterm window is open and has focus + $machine->waitForWindow(qr/testterm/); + $machine->waitUntilSucceeds("${pkgs.xdotool}/bin/xdotool search --sync --onlyvisible --class testterm windowfocus --sync"); + } + + # wait for reader to be ready + $machine->waitForFile("${readyFile}"); + $machine->sleep(1); + + # send all keys + foreach ((${send})) { $machine->sendKeys($_); }; + + # wait for result and check + $machine->waitForFile("${resultFile}"); + $machine->succeed("grep -q 'PASS:' ${resultFile}"); }; - } + }; $machine->waitForX; mkTest "VT keymap", "openvt -sw --"; - mkTest "Xorg keymap", "DISPLAY=:0 xterm -fullscreen -e"; + mkTest "Xorg keymap", "DISPLAY=:0 xterm -title testterm -class testterm -fullscreen -e"; ''; }; diff --git a/nixos/tests/kubernetes/certs.nix b/nixos/tests/kubernetes/certs.nix index d3eff910c467..520c728b65ee 100644 --- a/nixos/tests/kubernetes/certs.nix +++ b/nixos/tests/kubernetes/certs.nix @@ -6,29 +6,62 @@ kubelets }: let - runWithCFSSL = name: cmd: - builtins.fromJSON (builtins.readFile ( - pkgs.runCommand "${name}-cfss.json" { - buildInputs = [ pkgs.cfssl ]; - } "cfssl ${cmd} > $out" - )); - - writeCFSSL = content: - pkgs.runCommand content.name { - buildInputs = [ pkgs.cfssl ]; - } '' - mkdir -p $out - cd $out - cat ${writeFile content} | cfssljson -bare ${content.name} - ''; + runWithCFSSL = name: cmd: + let secrets = pkgs.runCommand "${name}-cfss.json" { + buildInputs = [ pkgs.cfssl pkgs.jq ]; + outputs = [ "out" "cert" "key" "csr" ]; + } + '' + ( + echo "${cmd}" + cfssl ${cmd} > tmp + cat tmp | jq -r .key > $key + cat tmp | jq -r .cert > $cert + cat tmp | jq -r .csr > $csr + + touch $out + ) 2>&1 | fold -w 80 -s + ''; + in { + key = secrets.key; + cert = secrets.cert; + csr = secrets.csr; + }; + + writeCFSSL = content: + pkgs.runCommand content.name { + buildInputs = [ pkgs.cfssl pkgs.jq ]; + } '' + mkdir -p $out + cd $out + + json=${pkgs.lib.escapeShellArg (builtins.toJSON content)} + + # for a given $field in the $json, treat the associated value as a + # file path and substitute the contents thereof into the $json + # object. + expandFileField() { + local field=$1 + if jq -e --arg field "$field" 'has($field)'; then + local path="$(echo "$json" | jq -r ".$field")" + json="$(echo "$json" | jq --arg val "$(cat "$path")" ".$field = \$val")" + fi + } + + expandFileField key + expandFileField ca + expandFileField cert + + echo "$json" | cfssljson -bare ${content.name} + ''; noCSR = content: pkgs.lib.filterAttrs (n: v: n != "csr") content; noKey = content: pkgs.lib.filterAttrs (n: v: n != "key") content; - writeFile = content: pkgs.writeText "content" ( - if pkgs.lib.isAttrs content then builtins.toJSON content - else toString content - ); + writeFile = content: + if pkgs.lib.isDerivation content + then content + else pkgs.writeText "content" (builtins.toJSON content); createServingCertKey = { ca, cn, hosts? [], size ? 2048, name ? cn }: noCSR ( diff --git a/nixos/tests/kubernetes/e2e.nix b/nixos/tests/kubernetes/e2e.nix index d9d7ba9bb2cc..175d8413045e 100644 --- a/nixos/tests/kubernetes/e2e.nix +++ b/nixos/tests/kubernetes/e2e.nix @@ -2,7 +2,7 @@ with import ./base.nix { inherit system; }; let domain = "my.zyx"; - certs = import ./certs.nix { externalDomain = domain; }; + certs = import ./certs.nix { externalDomain = domain; kubelets = ["machine1" "machine2"]; }; kubeconfig = pkgs.writeText "kubeconfig.json" (builtins.toJSON { apiVersion = "v1"; kind = "Config"; diff --git a/nixos/tests/kubernetes/rbac.nix b/nixos/tests/kubernetes/rbac.nix index 1966fed3a5fb..226808c4b263 100644 --- a/nixos/tests/kubernetes/rbac.nix +++ b/nixos/tests/kubernetes/rbac.nix @@ -12,7 +12,7 @@ let }); roRoleBinding = pkgs.writeText "ro-role-binding.json" (builtins.toJSON { - apiVersion = "rbac.authorization.k8s.io/v1beta1"; + apiVersion = "rbac.authorization.k8s.io/v1"; kind = "RoleBinding"; metadata = { name = "read-pods"; @@ -31,7 +31,7 @@ let }); roRole = pkgs.writeText "ro-role.json" (builtins.toJSON { - apiVersion = "rbac.authorization.k8s.io/v1beta1"; + apiVersion = "rbac.authorization.k8s.io/v1"; kind = "Role"; metadata = { name = "pod-reader"; diff --git a/nixos/tests/misc.nix b/nixos/tests/misc.nix index 4fd9466dc502..179c95e76436 100644 --- a/nixos/tests/misc.nix +++ b/nixos/tests/misc.nix @@ -96,7 +96,7 @@ import ./make-test.nix ({ pkgs, ...} : rec { $machine->succeed("systemctl start systemd-udev-settle.service"); subtest "udev-auto-load", sub { $machine->waitForUnit('systemd-udev-settle.service'); - $machine->succeed('lsmod | grep psmouse'); + $machine->succeed('lsmod | grep mousedev'); }; # Test whether systemd-tmpfiles-clean works. diff --git a/nixos/tests/openldap.nix b/nixos/tests/openldap.nix index 1178701c609e..1eaf87a8eaa8 100644 --- a/nixos/tests/openldap.nix +++ b/nixos/tests/openldap.nix @@ -1,5 +1,5 @@ import ./make-test.nix { - name = "dovecot"; + name = "openldap"; machine = { pkgs, ... }: { services.openldap = { @@ -28,8 +28,8 @@ import ./make-test.nix { }; testScript = '' - $machine->succeed('systemctl status openldap.service'); $machine->waitForUnit('openldap.service'); + $machine->succeed('systemctl status openldap.service'); $machine->succeed('ldapsearch -LLL -D "cn=root,dc=example" -w notapassword -b "dc=example"'); ''; } diff --git a/nixos/tests/osquery.nix b/nixos/tests/osquery.nix new file mode 100644 index 000000000000..281dbcff6643 --- /dev/null +++ b/nixos/tests/osquery.nix @@ -0,0 +1,28 @@ +import ./make-test.nix ({ pkgs, lib, ... }: + +with lib; + +{ + name = "osquery"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ ma27 ]; + }; + + machine = { + services.osquery.enable = true; + services.osquery.loggerPath = "/var/log/osquery/logs"; + services.osquery.pidfile = "/var/run/osqueryd.pid"; + }; + + testScript = '' + $machine->start; + $machine->waitForUnit("osqueryd.service"); + + $machine->succeed("echo 'SELECT address FROM etc_hosts LIMIT 1;' | osqueryi | grep '127.0.0.1'"); + $machine->succeed( + "echo 'SELECT value FROM osquery_flags WHERE name = \"logger_path\";' | osqueryi | grep /var/log/osquery/logs" + ); + + $machine->succeed("echo 'SELECT value FROM osquery_flags WHERE name = \"pidfile\";' | osqueryi | grep /var/run/osqueryd.pid"); + ''; +}) diff --git a/nixos/tests/predictable-interface-names.nix b/nixos/tests/predictable-interface-names.nix index b4c2039923cf..0b431034a7a9 100644 --- a/nixos/tests/predictable-interface-names.nix +++ b/nixos/tests/predictable-interface-names.nix @@ -1,27 +1,24 @@ -{ system ? builtins.currentSystem -, pkgs ? import ../.. { inherit system; } -}: -with import ../lib/testing.nix { inherit system; }; -let boolToString = x: if x then "yes" else "no"; in -let testWhenSetTo = predictable: withNetworkd: -makeTest { - name = "${if predictable then "" else "un"}predictableInterfaceNames${if withNetworkd then "-with-networkd" else ""}"; - meta = {}; +{ system ? builtins.currentSystem }: - machine = { config, pkgs, ... }: { - networking.usePredictableInterfaceNames = pkgs.stdenv.lib.mkForce predictable; - networking.useNetworkd = withNetworkd; - networking.dhcpcd.enable = !withNetworkd; - }; +let + inherit (import ../lib/testing.nix { inherit system; }) makeTest pkgs; +in pkgs.lib.listToAttrs (pkgs.lib.crossLists (predictable: withNetworkd: { + name = pkgs.lib.optionalString (!predictable) "un" + "predictable" + + pkgs.lib.optionalString withNetworkd "Networkd"; + value = makeTest { + name = "${if predictable then "" else "un"}predictableInterfaceNames${if withNetworkd then "-with-networkd" else ""}"; + meta = {}; + + machine = { config, lib, ... }: { + networking.usePredictableInterfaceNames = lib.mkForce predictable; + networking.useNetworkd = withNetworkd; + networking.dhcpcd.enable = !withNetworkd; + }; - testScript = '' - print $machine->succeed("ip link"); - $machine->succeed("ip link show ${if predictable then "ens3" else "eth0"}"); - $machine->fail("ip link show ${if predictable then "eth0" else "ens3"}"); - ''; -}; in -with pkgs.stdenv.lib.lists; -with pkgs.stdenv.lib.attrsets; -listToAttrs (map (drv: nameValuePair drv.name drv) ( -crossLists testWhenSetTo [[true false] [true false]] -)) + testScript = '' + print $machine->succeed("ip link"); + $machine->succeed("ip link show ${if predictable then "ens3" else "eth0"}"); + $machine->fail("ip link show ${if predictable then "eth0" else "ens3"}"); + ''; + }; +}) [[true false] [true false]]) diff --git a/nixos/tests/printing.nix b/nixos/tests/printing.nix index 2d3ecaf94cfa..989008830613 100644 --- a/nixos/tests/printing.nix +++ b/nixos/tests/printing.nix @@ -39,7 +39,9 @@ import ./make-test.nix ({pkgs, ... }: { $client->waitForUnit("cups.service"); $client->sleep(10); # wait until cups is fully initialized $client->succeed("lpstat -r") =~ /scheduler is running/ or die; - $client->succeed("lpstat -H") =~ "localhost:631" or die; + # Test that UNIX socket is used for connections. + $client->succeed("lpstat -H") =~ "/var/run/cups/cups.sock" or die; + # Test that HTTP server is available too. $client->succeed("curl --fail http://localhost:631/"); $client->succeed("curl --fail http://server:631/"); $server->fail("curl --fail --connect-timeout 2 http://client:631/"); diff --git a/nixos/tests/prosody.nix b/nixos/tests/prosody.nix new file mode 100644 index 000000000000..fcebfaf74e12 --- /dev/null +++ b/nixos/tests/prosody.nix @@ -0,0 +1,75 @@ +import ./make-test.nix { + name = "prosody"; + + machine = { config, pkgs, ... }: { + services.prosody = { + enable = true; + # TODO: use a self-signed certificate + c2sRequireEncryption = false; + }; + environment.systemPackages = let + sendMessage = pkgs.writeScriptBin "send-message" '' + #!/usr/bin/env python3 + # Based on the sleekxmpp send_client example, look there for more details: + # https://github.com/fritzy/SleekXMPP/blob/develop/examples/send_client.py + import sleekxmpp + + class SendMsgBot(sleekxmpp.ClientXMPP): + """ + A basic SleekXMPP bot that will log in, send a message, + and then log out. + """ + def __init__(self, jid, password, recipient, message): + sleekxmpp.ClientXMPP.__init__(self, jid, password) + + self.recipient = recipient + self.msg = message + + self.add_event_handler("session_start", self.start, threaded=True) + + def start(self, event): + self.send_presence() + self.get_roster() + + self.send_message(mto=self.recipient, + mbody=self.msg, + mtype='chat') + + self.disconnect(wait=True) + + + if __name__ == '__main__': + xmpp = SendMsgBot("test1@localhost", "test1", "test2@localhost", "Hello World!") + xmpp.register_plugin('xep_0030') # Service Discovery + xmpp.register_plugin('xep_0199') # XMPP Ping + + # TODO: verify certificate + # If you want to verify the SSL certificates offered by a server: + # xmpp.ca_certs = "path/to/ca/cert" + + if xmpp.connect(('localhost', 5222)): + xmpp.process(block=True) + else: + print("Unable to connect.") + sys.exit(1) + ''; + in [ (pkgs.python3.withPackages (ps: [ ps.sleekxmpp ])) sendMessage ]; + }; + + testScript = '' + $machine->waitForUnit('prosody.service'); + $machine->succeed('prosodyctl status') =~ /Prosody is running/; + + # set password to 'test' (it's asked twice) + $machine->succeed('yes test1 | prosodyctl adduser test1@localhost'); + # set password to 'y' + $machine->succeed('yes | prosodyctl adduser test2@localhost'); + # correct password to 'test2' + $machine->succeed('yes test2 | prosodyctl passwd test2@localhost'); + + $machine->succeed("send-message"); + + $machine->succeed('prosodyctl deluser test1@localhost'); + $machine->succeed('prosodyctl deluser test2@localhost'); + ''; +} diff --git a/nixos/tests/strongswan-swanctl.nix b/nixos/tests/strongswan-swanctl.nix new file mode 100644 index 000000000000..021743021b40 --- /dev/null +++ b/nixos/tests/strongswan-swanctl.nix @@ -0,0 +1,148 @@ +# This strongswan-swanctl test is based on: +# https://www.strongswan.org/testing/testresults/swanctl/rw-psk-ipv4/index.html +# https://github.com/strongswan/strongswan/tree/master/testing/tests/swanctl/rw-psk-ipv4 +# +# The roadwarrior carol sets up a connection to gateway moon. The authentication +# is based on pre-shared keys and IPv4 addresses. Upon the successful +# establishment of the IPsec tunnels, the specified updown script automatically +# inserts iptables-based firewall rules that let pass the tunneled traffic. In +# order to test both tunnel and firewall, carol pings the client alice behind +# the gateway moon. +# +# alice moon carol +# eth1------vlan_0------eth1 eth2------vlan_1------eth1 +# 192.168.0.1 192.168.0.3 192.168.1.3 192.168.1.2 +# +# See the NixOS manual for how to run this test: +# https://nixos.org/nixos/manual/index.html#sec-running-nixos-tests-interactively + +import ./make-test.nix ({ pkgs, ...} : + +let + allowESP = "iptables --insert INPUT --protocol ESP --jump ACCEPT"; + + # Shared VPN settings: + vlan0 = "192.168.0.0/24"; + carolIp = "192.168.1.2"; + moonIp = "192.168.1.3"; + version = 2; + secret = "0sFpZAZqEN6Ti9sqt4ZP5EWcqx"; + esp_proposals = [ "aes128gcm128-x25519" ]; + proposals = [ "aes128-sha256-x25519" ]; +in { + name = "strongswan-swanctl"; + meta.maintainers = with pkgs.stdenv.lib.maintainers; [ basvandijk ]; + nodes = { + + alice = { nodes, ... } : { + virtualisation.vlans = [ 0 ]; + networking = { + dhcpcd.enable = false; + defaultGateway = "192.168.0.3"; + }; + }; + + moon = {pkgs, config, nodes, ...} : + let strongswan = config.services.strongswan-swanctl.package; + in { + virtualisation.vlans = [ 0 1 ]; + networking = { + dhcpcd.enable = false; + firewall = { + allowedUDPPorts = [ 4500 500 ]; + extraCommands = allowESP; + }; + nat = { + enable = true; + internalIPs = [ vlan0 ]; + internalInterfaces = [ "eth1" ]; + externalIP = moonIp; + externalInterface = "eth2"; + }; + }; + environment.systemPackages = [ strongswan ]; + services.strongswan-swanctl = { + enable = true; + swanctl = { + connections = { + "rw" = { + local_addrs = [ moonIp ]; + local."main" = { + auth = "psk"; + }; + remote."main" = { + auth = "psk"; + }; + children = { + "net" = { + local_ts = [ vlan0 ]; + updown = "${strongswan}/libexec/ipsec/_updown iptables"; + inherit esp_proposals; + }; + }; + inherit version; + inherit proposals; + }; + }; + secrets = { + ike."carol" = { + id."main" = carolIp; + inherit secret; + }; + }; + }; + }; + }; + + carol = {pkgs, config, nodes, ...} : + let strongswan = config.services.strongswan-swanctl.package; + in { + virtualisation.vlans = [ 1 ]; + networking = { + dhcpcd.enable = false; + firewall.extraCommands = allowESP; + }; + environment.systemPackages = [ strongswan ]; + services.strongswan-swanctl = { + enable = true; + swanctl = { + connections = { + "home" = { + local_addrs = [ carolIp ]; + remote_addrs = [ moonIp ]; + local."main" = { + auth = "psk"; + id = carolIp; + }; + remote."main" = { + auth = "psk"; + id = moonIp; + }; + children = { + "home" = { + remote_ts = [ vlan0 ]; + start_action = "trap"; + updown = "${strongswan}/libexec/ipsec/_updown iptables"; + inherit esp_proposals; + }; + }; + inherit version; + inherit proposals; + }; + }; + secrets = { + ike."moon" = { + id."main" = moonIp; + inherit secret; + }; + }; + }; + }; + }; + + }; + testScript = '' + startAll(); + $carol->waitUntilSucceeds("ping -c 1 alice"); + ''; +}) diff --git a/nixos/tests/systemd.nix b/nixos/tests/systemd.nix index 2df6f341c4ef..65aa553b3148 100644 --- a/nixos/tests/systemd.nix +++ b/nixos/tests/systemd.nix @@ -46,6 +46,8 @@ import ./make-test.nix { testScript = '' $machine->waitForX; + # wait for user services + $machine->waitForUnit("default.target","alice"); # Regression test for https://github.com/NixOS/nixpkgs/issues/35415 subtest "configuration files are recognized by systemd", sub { diff --git a/nixos/tests/transmission.nix b/nixos/tests/transmission.nix new file mode 100644 index 000000000000..34c49bd7f15b --- /dev/null +++ b/nixos/tests/transmission.nix @@ -0,0 +1,21 @@ +import ./make-test.nix ({ pkgs, ...} : { + name = "transmission"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ coconnor ]; + }; + + machine = { config, pkgs, ... }: { + imports = [ ../modules/profiles/minimal.nix ]; + + networking.firewall.allowedTCPPorts = [ 9091 ]; + + services.transmission.enable = true; + }; + + testScript = + '' + startAll; + $machine->waitForUnit("transmission"); + $machine->shutdown; + ''; +}) diff --git a/nixos/tests/udisks2.nix b/nixos/tests/udisks2.nix index 72d51c0051c0..70a999267a54 100644 --- a/nixos/tests/udisks2.nix +++ b/nixos/tests/udisks2.nix @@ -37,7 +37,8 @@ in $machine->fail("udisksctl info -b /dev/sda1"); # Attach a USB stick and wait for it to show up. - $machine->sendMonitorCommand("usb_add disk:$stick"); + $machine->sendMonitorCommand("drive_add 0 id=stick,if=none,file=$stick,format=raw"); + $machine->sendMonitorCommand("device_add usb-storage,id=stick,drive=stick"); $machine->waitUntilSucceeds("udisksctl info -b /dev/sda1"); $machine->succeed("udisksctl info -b /dev/sda1 | grep 'IdLabel:.*USBSTICK'"); @@ -52,7 +53,7 @@ in $machine->fail("[ -d /run/media/alice/USBSTICK ]"); # Remove the USB stick. - $machine->sendMonitorCommand("usb_del 0.3"); # FIXME + $machine->sendMonitorCommand("device_del stick"); $machine->waitUntilFails("udisksctl info -b /dev/sda1"); $machine->fail("[ -e /dev/sda ]"); ''; diff --git a/nixos/tests/vault.nix b/nixos/tests/vault.nix index 2c08d06f286b..515d5c8bac25 100644 --- a/nixos/tests/vault.nix +++ b/nixos/tests/vault.nix @@ -17,7 +17,7 @@ import ./make-test.nix ({ pkgs, ... }: $machine->waitForUnit('multi-user.target'); $machine->waitForUnit('vault.service'); $machine->waitForOpenPort(8200); - $machine->succeed('vault init'); - $machine->succeed('vault status | grep "Sealed: true"'); + $machine->succeed('vault operator init'); + $machine->succeed('vault status | grep Sealed | grep true'); ''; }) diff --git a/nixos/tests/virtualbox.nix b/nixos/tests/virtualbox.nix index 5574293ba377..249571fcedec 100644 --- a/nixos/tests/virtualbox.nix +++ b/nixos/tests/virtualbox.nix @@ -43,6 +43,9 @@ let "init=${pkgs.writeScript "mini-init.sh" miniInit}" ]; + # XXX: Remove this once TSS location detection has been fixed in VirtualBox + boot.kernelPackages = pkgs.linuxPackages_4_9; + fileSystems."/" = { device = "vboxshare"; fsType = "vboxsf"; diff --git a/nixos/tests/xautolock.nix b/nixos/tests/xautolock.nix new file mode 100644 index 000000000000..ee46d9e05b06 --- /dev/null +++ b/nixos/tests/xautolock.nix @@ -0,0 +1,24 @@ +import ./make-test.nix ({ pkgs, lib, ... }: + +with lib; + +{ + name = "xautolock"; + meta.maintainers = with pkgs.stdenv.lib.maintainers; [ ma27 ]; + + nodes.machine = { + imports = [ ./common/x11.nix ./common/user-account.nix ]; + + services.xserver.displayManager.auto.user = "bob"; + services.xserver.xautolock.enable = true; + services.xserver.xautolock.time = 1; + }; + + testScript = '' + $machine->start; + $machine->waitForX; + $machine->mustFail("pgrep xlock"); + $machine->sleep(120); + $machine->mustSucceed("pgrep xlock"); + ''; +}) |