diff options
Diffstat (limited to 'nixos')
98 files changed, 2961 insertions, 529 deletions
diff --git a/nixos/doc/manual/from_md/installation/installing-from-other-distro.section.xml b/nixos/doc/manual/from_md/installation/installing-from-other-distro.section.xml index 525531a478135..024a24379dd6d 100644 --- a/nixos/doc/manual/from_md/installation/installing-from-other-distro.section.xml +++ b/nixos/doc/manual/from_md/installation/installing-from-other-distro.section.xml @@ -248,7 +248,7 @@ $ nix-env -p /nix/var/nix/profiles/system -f '<nixpkgs/nixos>' -I nixos-co (since your Nix install was probably single user): </para> <programlisting> -$ sudo chown -R 0.0 /nix +$ sudo chown -R 0:0 /nix </programlisting> </listitem> <listitem> diff --git a/nixos/doc/manual/from_md/release-notes/rl-2111.section.xml b/nixos/doc/manual/from_md/release-notes/rl-2111.section.xml index b61a0268dee2c..d9ebbe74d54fa 100644 --- a/nixos/doc/manual/from_md/release-notes/rl-2111.section.xml +++ b/nixos/doc/manual/from_md/release-notes/rl-2111.section.xml @@ -569,8 +569,9 @@ <listitem> <para> The NixOS VM test framework, - <literal>pkgs.nixosTest</literal>/<literal>make-test-python.nix</literal>, - now requires detaching commands such as + <literal>pkgs.nixosTest</literal>/<literal>make-test-python.nix</literal> + (<literal>pkgs.testers.nixosTest</literal> since 22.05), now + requires detaching commands such as <literal>succeed("foo &")</literal> and <literal>succeed("foo | xclip -i")</literal> to close stdout. This can be done with a redirect such as diff --git a/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml b/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml index c6ac5928d1efa..6092a0477a5c9 100644 --- a/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml +++ b/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml @@ -45,6 +45,33 @@ </listitem> <listitem> <para> + <literal>stdenv.mkDerivation</literal> now supports a + self-referencing <literal>finalAttrs:</literal> parameter + containing the final <literal>mkDerivation</literal> arguments + including overrides. <literal>drv.overrideAttrs</literal> now + supports two parameters + <literal>finalAttrs: previousAttrs:</literal>. This allows + packaging configuration to be overridden in a consistent + manner by providing an alternative to + <literal>rec {}</literal> syntax. + </para> + <para> + Additionally, <literal>passthru</literal> can now reference + <literal>finalAttrs.finalPackage</literal> containing the + final package, including attributes such as the output paths + and <literal>overrideAttrs</literal>. + </para> + <para> + New language integrations can be simplified by overriding a + <quote>prototype</quote> package containing the + language-specific logic. This removes the need for a extra + layer of overriding for the <quote>generic builder</quote> + arguments, thus removing a usability problem and source of + error. + </para> + </listitem> + <listitem> + <para> PHP 8.1 is now available </para> </listitem> @@ -129,6 +156,14 @@ default. </para> </listitem> + <listitem> + <para> + The GNOME and Plasma installation CDs now use + <literal>pkgs.calamares</literal> and + <literal>pkgs.calamares-nixos-extensions</literal> to allow + users to easily install and set up NixOS with a GUI. + </para> + </listitem> </itemizedlist> </section> <section xml:id="sec-release-22.05-new-services"> @@ -210,6 +245,14 @@ </listitem> <listitem> <para> + <link xlink:href="https://snipeitapp.com">Snipe-IT</link>, a + free open source IT asset/license management system. Available + as + <link xlink:href="options.html#opt-services.snipe-it.enable">services.snipe-it</link>. + </para> + </listitem> + <listitem> + <para> <link xlink:href="https://github.com/ngoduykhanh/PowerDNS-Admin">PowerDNS-Admin</link>, a web interface for the PowerDNS server. Available at <link xlink:href="options.html#opt-services.powerdns-admin.enable">services.powerdns-admin</link>. @@ -425,6 +468,12 @@ <link xlink:href="options.html#opt-services.nifi.enable">services.nifi</link>. </para> </listitem> + <listitem> + <para> + <link xlink:href="https://kanidm.github.io/kanidm/stable/">kanidm</link>, + an identity management server written in Rust. + </para> + </listitem> </itemizedlist> </section> <section xml:id="sec-release-22.05-incompatibilities"> @@ -847,6 +896,11 @@ to the new location if the <literal>stateVersion</literal> is updated. </para> + <para> + As of Synapse 1.58.0, the old groups/communities feature has + been disabled by default. It will be completely removed with + Synapse 1.61.0. + </para> </listitem> <listitem> <para> @@ -1126,6 +1180,16 @@ </listitem> <listitem> <para> + <literal>teleport</literal> has been upgraded to major version + 9. Please see upstream + <link xlink:href="https://goteleport.com/docs/setup/operations/upgrading/">upgrade + instructions</link> and + <link xlink:href="https://goteleport.com/docs/changelog/#900">release + notes</link>. + </para> + </listitem> + <listitem> + <para> For <literal>pkgs.python3.pkgs.ipython</literal>, its direct dependency <literal>pkgs.python3.pkgs.matplotlib-inline</literal> (which @@ -1407,6 +1471,16 @@ </listitem> <listitem> <para> + <literal>pkgs.minetestclient_4</literal> and + <literal>pkgs.minetestserver_4</literal> have been removed, as + the last 4.x release was in 2018. + <literal>pkgs.minetestclient</literal> (equivalent to + <literal>pkgs.minetest</literal> ) and + <literal>pkgs.minetestserver</literal> can be used instead. + </para> + </listitem> + <listitem> + <para> <literal>pkgs.noto-fonts-cjk</literal> is now deprecated in favor of <literal>pkgs.noto-fonts-cjk-sans</literal> and <literal>pkgs.noto-fonts-cjk-serif</literal> because they each @@ -2240,6 +2314,14 @@ </listitem> <listitem> <para> + The <literal>phpPackages.box</literal> package has been + updated from 2.7.5 to 3.16.0. See the + <link xlink:href="https://github.com/box-project/box/blob/master/UPGRADE.md#from-27-to-30">upgrade + guide</link> for more details. + </para> + </listitem> + <listitem> + <para> The <literal>zrepl</literal> package has been updated from 0.4.0 to 0.5: </para> @@ -2430,6 +2512,21 @@ hosts. </para> </listitem> + <listitem> + <para> + The option + <link xlink:href="options.html#opt-networking.useDHCP">networking.useDHCP</link> + isn’t deprecated anymore. When using + <link xlink:href="options.html#opt-networking.useNetworkd"><literal>systemd-networkd</literal></link>, + a generic <literal>.network</literal>-unit is added which + enables DHCP for each interface matching + <literal>en*</literal>, <literal>eth*</literal> or + <literal>wl*</literal> with priority 99 (which means that it + doesn’t have any effect if such an interface is matched by a + <literal>.network-</literal>unit with a lower priority). In + case of scripted networking, no behavior was changed. + </para> + </listitem> </itemizedlist> </section> </section> diff --git a/nixos/doc/manual/installation/installing-from-other-distro.section.md b/nixos/doc/manual/installation/installing-from-other-distro.section.md index d9060eb89c372..fa8806f791d52 100644 --- a/nixos/doc/manual/installation/installing-from-other-distro.section.md +++ b/nixos/doc/manual/installation/installing-from-other-distro.section.md @@ -177,7 +177,7 @@ The first steps to all these are the same: was probably single user): ```ShellSession - $ sudo chown -R 0.0 /nix + $ sudo chown -R 0:0 /nix ``` 1. Set up the `/etc/NIXOS` and `/etc/NIXOS_LUSTRATE` files: diff --git a/nixos/doc/manual/release-notes/rl-2111.section.md b/nixos/doc/manual/release-notes/rl-2111.section.md index 310d32cfdd721..e673d6721a38c 100644 --- a/nixos/doc/manual/release-notes/rl-2111.section.md +++ b/nixos/doc/manual/release-notes/rl-2111.section.md @@ -166,7 +166,7 @@ In addition to numerous new and upgraded packages, this release has the followin ## Backward Incompatibilities {#sec-release-21.11-incompatibilities} -- The NixOS VM test framework, `pkgs.nixosTest`/`make-test-python.nix`, now requires detaching commands such as `succeed("foo &")` and `succeed("foo | xclip -i")` to close stdout. +- The NixOS VM test framework, `pkgs.nixosTest`/`make-test-python.nix` (`pkgs.testers.nixosTest` since 22.05), now requires detaching commands such as `succeed("foo &")` and `succeed("foo | xclip -i")` to close stdout. This can be done with a redirect such as `succeed("foo >&2 &")`. This breaking change was necessitated by a race condition causing tests to fail or hang. It applies to all methods that invoke commands on the nodes, including `execute`, `succeed`, `fail`, `wait_until_succeeds`, `wait_until_fails`. diff --git a/nixos/doc/manual/release-notes/rl-2205.section.md b/nixos/doc/manual/release-notes/rl-2205.section.md index b10c11bc4f449..968d51fc45c19 100644 --- a/nixos/doc/manual/release-notes/rl-2205.section.md +++ b/nixos/doc/manual/release-notes/rl-2205.section.md @@ -17,6 +17,21 @@ In addition to numerous new and upgraded packages, this release has the followin - GNOME has been upgraded to 42. Please take a look at their [Release Notes](https://release.gnome.org/42/) for details. Notably, it replaces gedit with GNOME Text Editor, GNOME Terminal with GNOME Console (formerly King’s Cross), and GNOME Screenshot with a tool built into the Shell. +- `stdenv.mkDerivation` now supports a self-referencing `finalAttrs:` parameter + containing the final `mkDerivation` arguments including overrides. + `drv.overrideAttrs` now supports two parameters `finalAttrs: previousAttrs:`. + This allows packaging configuration to be overridden in a consistent manner by + providing an alternative to `rec {}` syntax. + + Additionally, `passthru` can now reference `finalAttrs.finalPackage` containing + the final package, including attributes such as the output paths and + `overrideAttrs`. + + New language integrations can be simplified by overriding a "prototype" + package containing the language-specific logic. This removes the need for a + extra layer of overriding for the "generic builder" arguments, thus removing a + usability problem and source of error. + - PHP 8.1 is now available - Mattermost has been updated to extended support release 6.3, as the previously packaged extended support release 5.37 is [reaching its end of life](https://docs.mattermost.com/upgrade/extended-support-release.html). @@ -40,6 +55,8 @@ In addition to numerous new and upgraded packages, this release has the followin - The default GHC version has been updated from 8.10.7 to 9.0.2. `pkgs.haskellPackages` and `pkgs.ghc` will now use this version by default. +- The GNOME and Plasma installation CDs now use `pkgs.calamares` and `pkgs.calamares-nixos-extensions` to allow users to easily install and set up NixOS with a GUI. + ## New Services {#sec-release-22.05-new-services} - [aesmd](https://github.com/intel/linux-sgx#install-the-intelr-sgx-psw), the Intel SGX Architectural Enclave Service Manager. Available as [services.aesmd](#opt-services.aesmd.enable). @@ -62,6 +79,8 @@ In addition to numerous new and upgraded packages, this release has the followin - [ergochat](https://ergo.chat), a modern IRC with IRCv3 features. Available as [services.ergochat](options.html#opt-services.ergochat.enable). +- [Snipe-IT](https://snipeitapp.com), a free open source IT asset/license management system. Available as [services.snipe-it](options.html#opt-services.snipe-it.enable). + - [PowerDNS-Admin](https://github.com/ngoduykhanh/PowerDNS-Admin), a web interface for the PowerDNS server. Available at [services.powerdns-admin](options.html#opt-services.powerdns-admin.enable). - [pgadmin4](https://github.com/postgres/pgadmin4), an admin interface for the PostgreSQL database. Available at [services.pgadmin](options.html#opt-services.pgadmin.enable). @@ -120,6 +139,8 @@ In addition to numerous new and upgraded packages, this release has the followin - [nifi](https://nifi.apache.org), an easy to use, powerful, and reliable system to process and distribute data. Available as [services.nifi](options.html#opt-services.nifi.enable). +- [kanidm](https://kanidm.github.io/kanidm/stable/), an identity management server written in Rust. + <!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. --> ## Backward Incompatibilities {#sec-release-22.05-incompatibilities} @@ -347,6 +368,8 @@ In addition to numerous new and upgraded packages, this release has the followin `media_store_path` was changed from `${dataDir}/media` to `${dataDir}/media_store` if `system.stateVersion` is at least `22.05`. Files will need to be manually moved to the new location if the `stateVersion` is updated. + As of Synapse 1.58.0, the old groups/communities feature has been disabled by default. It will be completely removed with Synapse 1.61.0. + - The Keycloak package (`pkgs.keycloak`) has been switched from the Wildfly version, which will soon be deprecated, to the Quarkus based version. The Keycloak service (`services.keycloak`) has been updated @@ -469,6 +492,8 @@ In addition to numerous new and upgraded packages, this release has the followin - The `autorestic` package has been upgraded from 1.3.0 to 1.5.0 which introduces breaking changes in config file, check [their migration guide](https://autorestic.vercel.app/migration/1.4_1.5) for more details. +- `teleport` has been upgraded to major version 9. Please see upstream [upgrade instructions](https://goteleport.com/docs/setup/operations/upgrading/) and [release notes](https://goteleport.com/docs/changelog/#900). + - For `pkgs.python3.pkgs.ipython`, its direct dependency `pkgs.python3.pkgs.matplotlib-inline` (which is really an adapter to integrate matplotlib in ipython if it is installed) does not depend on `pkgs.python3.pkgs.matplotlib` anymore. @@ -552,6 +577,8 @@ In addition to numerous new and upgraded packages, this release has the followin - `pkgs.pgadmin` now refers to `pkgs.pgadmin4`. `pgadmin3` has been removed. +- `pkgs.minetestclient_4` and `pkgs.minetestserver_4` have been removed, as the last 4.x release was in 2018. `pkgs.minetestclient` (equivalent to `pkgs.minetest` ) and `pkgs.minetestserver` can be used instead. + - `pkgs.noto-fonts-cjk` is now deprecated in favor of `pkgs.noto-fonts-cjk-sans` and `pkgs.noto-fonts-cjk-serif` because they each have different release schedules. To maintain compatibility with prior releases of Nixpkgs, @@ -804,6 +831,8 @@ In addition to numerous new and upgraded packages, this release has the followin - The `vscode-extensions.ionide.ionide-fsharp` package has been updated to 6.0.0 and now requires .NET 6.0. +- The `phpPackages.box` package has been updated from 2.7.5 to 3.16.0. See the [upgrade guide](https://github.com/box-project/box/blob/master/UPGRADE.md#from-27-to-30) for more details. + - The `zrepl` package has been updated from 0.4.0 to 0.5: - The RPC protocol version was bumped; all zrepl daemons in a setup must be updated and restarted before replication can resume. @@ -858,4 +887,11 @@ In addition to numerous new and upgraded packages, this release has the followin `true` starting with NixOS 22.11. Enable it explicitly if you need to control Snapserver remotely or connect streamig clients from other hosts. +- The option [networking.useDHCP](options.html#opt-networking.useDHCP) isn't deprecated anymore. + When using [`systemd-networkd`](options.html#opt-networking.useNetworkd), a generic + `.network`-unit is added which enables DHCP for each interface matching `en*`, `eth*` + or `wl*` with priority 99 (which means that it doesn't have any effect if such an interface is matched + by a `.network-`unit with a lower priority). In case of scripted networking, no behavior + was changed. + <!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. --> diff --git a/nixos/lib/build-vms.nix b/nixos/lib/build-vms.nix index 05d9ce89dbdc3..18af49db17777 100644 --- a/nixos/lib/build-vms.nix +++ b/nixos/lib/build-vms.nix @@ -38,7 +38,7 @@ rec { { key = "no-revision"; # Make the revision metadata constant, in order to avoid needless retesting. # The human version (e.g. 21.05-pre) is left as is, because it is useful - # for external modules that test with e.g. nixosTest and rely on that + # for external modules that test with e.g. testers.nixosTest and rely on that # version number. config.system.nixos.revision = mkForce "constant-nixos-revision"; } diff --git a/nixos/lib/testing-python.nix b/nixos/lib/testing-python.nix index cd2bb2f9d4d4c..4ab3cf53045af 100644 --- a/nixos/lib/testing-python.nix +++ b/nixos/lib/testing-python.nix @@ -119,6 +119,7 @@ rec { passthru = passthru // { inherit nodes; }; + meta.mainProgram = "nixos-test-driver"; } '' mkdir -p $out/bin diff --git a/nixos/maintainers/scripts/lxd/lxd-image.nix b/nixos/maintainers/scripts/lxd/lxd-image.nix index c76b9fcc7f779..6aa3f2f558471 100644 --- a/nixos/maintainers/scripts/lxd/lxd-image.nix +++ b/nixos/maintainers/scripts/lxd/lxd-image.nix @@ -27,7 +27,7 @@ with lib; networking.useDHCP = false; networking.interfaces.eth0.useDHCP = true; - # As this is intended as a stadalone image, undo some of the minimal profile stuff + # As this is intended as a standalone image, undo some of the minimal profile stuff documentation.enable = true; documentation.nixos.enable = true; environment.noXlibs = false; diff --git a/nixos/modules/hardware/keyboard/uhk.nix b/nixos/modules/hardware/keyboard/uhk.nix new file mode 100644 index 0000000000000..bf2d739c3a97e --- /dev/null +++ b/nixos/modules/hardware/keyboard/uhk.nix @@ -0,0 +1,21 @@ +{ config, lib, pkgs, ... }: + +with lib; +let + cfg = config.hardware.keyboard.uhk; +in +{ + options.hardware.keyboard.uhk = { + enable = mkEnableOption '' + non-root access to the firmware of UHK keyboards. + You need it when you want to flash a new firmware on the keyboard. + Access to the keyboard is granted to users in the "input" group. + You may want to install the uhk-agent package. + ''; + + }; + + config = mkIf cfg.enable { + services.udev.packages = [ pkgs.uhk-udev-rules ]; + }; +} diff --git a/nixos/modules/installer/cd-dvd/installation-cd-base.nix b/nixos/modules/installer/cd-dvd/installation-cd-base.nix index 618057618d0c3..3f92b779d60a2 100644 --- a/nixos/modules/installer/cd-dvd/installation-cd-base.nix +++ b/nixos/modules/installer/cd-dvd/installation-cd-base.nix @@ -46,5 +46,5 @@ with lib; done ''; - system.stateVersion = mkDefault "18.03"; + system.stateVersion = lib.mkDefault lib.trivial.release; } diff --git a/nixos/modules/installer/cd-dvd/installation-cd-graphical-base.nix b/nixos/modules/installer/cd-dvd/installation-cd-graphical-base.nix index fa19daf132800..0e4feba228234 100644 --- a/nixos/modules/installer/cd-dvd/installation-cd-graphical-base.nix +++ b/nixos/modules/installer/cd-dvd/installation-cd-graphical-base.nix @@ -35,22 +35,28 @@ with lib; # Enable sound in graphical iso's. hardware.pulseaudio.enable = true; - environment.systemPackages = [ + # Spice guest additions + services.spice-vdagentd.enable = true; + + # Enable plymouth + boot.plymouth.enable = true; + + environment.defaultPackages = with pkgs; [ # Include gparted for partitioning disks. - pkgs.gparted + gparted # Include some editors. - pkgs.vim - pkgs.bvi # binary editor - pkgs.joe + vim + nano # Include some version control tools. - pkgs.git + git + rsync # Firefox for reading the manual. - pkgs.firefox + firefox - pkgs.glxinfo + glxinfo ]; } diff --git a/nixos/modules/installer/cd-dvd/installation-cd-graphical-calamares-gnome.nix b/nixos/modules/installer/cd-dvd/installation-cd-graphical-calamares-gnome.nix new file mode 100644 index 0000000000000..95aeca1a928a6 --- /dev/null +++ b/nixos/modules/installer/cd-dvd/installation-cd-graphical-calamares-gnome.nix @@ -0,0 +1,59 @@ +# This module defines a NixOS installation CD that contains GNOME. + +{ pkgs, ... }: + +{ + imports = [ ./installation-cd-graphical-calamares.nix ]; + + isoImage.edition = "gnome"; + + services.xserver.desktopManager.gnome = { + # Add Firefox and other tools useful for installation to the launcher + favoriteAppsOverride = '' + [org.gnome.shell] + favorite-apps=[ 'firefox.desktop', 'nixos-manual.desktop', 'org.gnome.Console.desktop', 'org.gnome.Nautilus.desktop', 'gparted.desktop', 'io.calamares.calamares.desktop' ] + ''; + + # Override GNOME defaults to disable GNOME tour and disable suspend + extraGSettingsOverrides = '' + [org.gnome.shell] + welcome-dialog-last-shown-version='9999999999' + + [org.gnome.settings-daemon.plugins.power] + sleep-inactive-ac-type='nothing' + sleep-inactive-battery-type='nothing' + ''; + + extraGSettingsOverridePackages = [ pkgs.gnome.gnome-settings-daemon ]; + + enable = true; + }; + + # Theme calamares with GNOME theme + qt5 = { + enable = true; + platformTheme = "gnome"; + }; + + # Fix scaling for calamares on wayland + environment.variables = { + QT_QPA_PLATFORM = "$([[ $XDG_SESSION_TYPE = \"wayland\" ]] && echo \"wayland\")"; + }; + + services.xserver.displayManager = { + gdm = { + enable = true; + # autoSuspend makes the machine automatically suspend after inactivity. + # It's possible someone could/try to ssh'd into the machine and obviously + # have issues because it's inactive. + # See: + # * https://github.com/NixOS/nixpkgs/pull/63790 + # * https://gitlab.gnome.org/GNOME/gnome-control-center/issues/22 + autoSuspend = false; + }; + autoLogin = { + enable = true; + user = "nixos"; + }; + }; +} diff --git a/nixos/modules/installer/cd-dvd/installation-cd-graphical-calamares-plasma5.nix b/nixos/modules/installer/cd-dvd/installation-cd-graphical-calamares-plasma5.nix new file mode 100644 index 0000000000000..a4c46d58c85a4 --- /dev/null +++ b/nixos/modules/installer/cd-dvd/installation-cd-graphical-calamares-plasma5.nix @@ -0,0 +1,49 @@ +# This module defines a NixOS installation CD that contains X11 and +# Plasma 5. + +{ pkgs, ... }: + +{ + imports = [ ./installation-cd-graphical-calamares.nix ]; + + isoImage.edition = "plasma5"; + + services.xserver = { + desktopManager.plasma5 = { + enable = true; + }; + + # Automatically login as nixos. + displayManager = { + sddm.enable = true; + autoLogin = { + enable = true; + user = "nixos"; + }; + }; + }; + + environment.systemPackages = with pkgs; [ + # Graphical text editor + kate + ]; + + system.activationScripts.installerDesktop = let + + # Comes from documentation.nix when xserver and nixos.enable are true. + manualDesktopFile = "/run/current-system/sw/share/applications/nixos-manual.desktop"; + + homeDir = "/home/nixos/"; + desktopDir = homeDir + "Desktop/"; + + in '' + mkdir -p ${desktopDir} + chown nixos ${homeDir} ${desktopDir} + + ln -sfT ${manualDesktopFile} ${desktopDir + "nixos-manual.desktop"} + ln -sfT ${pkgs.gparted}/share/applications/gparted.desktop ${desktopDir + "gparted.desktop"} + ln -sfT ${pkgs.konsole}/share/applications/org.kde.konsole.desktop ${desktopDir + "org.kde.konsole.desktop"} + ln -sfT ${pkgs.calamares-nixos}/share/applications/io.calamares.calamares.desktop ${desktopDir + "io.calamares.calamares.desktop"} + ''; + +} diff --git a/nixos/modules/installer/cd-dvd/installation-cd-graphical-calamares.nix b/nixos/modules/installer/cd-dvd/installation-cd-graphical-calamares.nix new file mode 100644 index 0000000000000..8a6d30d1801a1 --- /dev/null +++ b/nixos/modules/installer/cd-dvd/installation-cd-graphical-calamares.nix @@ -0,0 +1,20 @@ +# This module adds the calamares installer to the basic graphical NixOS +# installation CD. + +{ pkgs, ... }: +let + calamares-nixos-autostart = pkgs.makeAutostartItem { name = "io.calamares.calamares"; package = pkgs.calamares-nixos; }; +in +{ + imports = [ ./installation-cd-graphical-base.nix ]; + + environment.systemPackages = with pkgs; [ + # Calamares for graphical installation + libsForQt5.kpmcore + calamares-nixos + calamares-nixos-autostart + calamares-nixos-extensions + # Needed for calamares QML module packagechooserq + libsForQt5.full + ]; +} diff --git a/nixos/modules/installer/cd-dvd/installation-cd-graphical-gnome.nix b/nixos/modules/installer/cd-dvd/installation-cd-graphical-gnome.nix index 303493741f3d0..573b31b439c2d 100644 --- a/nixos/modules/installer/cd-dvd/installation-cd-graphical-gnome.nix +++ b/nixos/modules/installer/cd-dvd/installation-cd-graphical-gnome.nix @@ -1,8 +1,6 @@ # This module defines a NixOS installation CD that contains GNOME. -{ lib, ... }: - -with lib; +{ ... }: { imports = [ ./installation-cd-graphical-base.nix ]; diff --git a/nixos/modules/installer/cd-dvd/installation-cd-graphical-plasma5.nix b/nixos/modules/installer/cd-dvd/installation-cd-graphical-plasma5.nix index 098c2b2870b04..5c7617c9f8c1a 100644 --- a/nixos/modules/installer/cd-dvd/installation-cd-graphical-plasma5.nix +++ b/nixos/modules/installer/cd-dvd/installation-cd-graphical-plasma5.nix @@ -1,9 +1,7 @@ # This module defines a NixOS installation CD that contains X11 and # Plasma 5. -{ config, lib, pkgs, ... }: - -with lib; +{ pkgs, ... }: { imports = [ ./installation-cd-graphical-base.nix ]; diff --git a/nixos/modules/installer/tools/nixos-generate-config.pl b/nixos/modules/installer/tools/nixos-generate-config.pl index fb5d3ba473255..b74ec838df42f 100644 --- a/nixos/modules/installer/tools/nixos-generate-config.pl +++ b/nixos/modules/installer/tools/nixos-generate-config.pl @@ -581,17 +581,19 @@ ${\join "", (map { " $_\n" } (uniq @attrs))}} EOF sub generateNetworkingDhcpConfig { + # FIXME disable networking.useDHCP by default when switching to networkd. my $config = <<EOF; - # The global useDHCP flag is deprecated, therefore explicitly set to false here. - # Per-interface useDHCP will be mandatory in the future, so this generated config - # replicates the default behaviour. - networking.useDHCP = lib.mkDefault false; + # Enables DHCP on each ethernet and wireless interface. In case of scripted networking + # (the default) this is the recommended approach. When using systemd-networkd it's + # still possible to use this option, but it's recommended to use it in conjunction + # with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`. + networking.useDHCP = lib.mkDefault true; EOF foreach my $path (glob "/sys/class/net/*") { my $dev = basename($path); if ($dev ne "lo") { - $config .= " networking.interfaces.$dev.useDHCP = lib.mkDefault true;\n"; + $config .= " # networking.interfaces.$dev.useDHCP = lib.mkDefault true;\n"; } } diff --git a/nixos/modules/installer/tools/tools.nix b/nixos/modules/installer/tools/tools.nix index bf5ec0f9690b0..04be272742ce1 100644 --- a/nixos/modules/installer/tools/tools.nix +++ b/nixos/modules/installer/tools/tools.nix @@ -34,7 +34,7 @@ let name = "nixos-generate-config"; src = ./nixos-generate-config.pl; perl = "${pkgs.perl.withPackages (p: [ p.FileSlurp ])}/bin/perl"; - detectvirt = "${pkgs.systemd}/bin/systemd-detect-virt"; + detectvirt = "${config.systemd.package}/bin/systemd-detect-virt"; btrfs = "${pkgs.btrfs-progs}/bin/btrfs"; inherit (config.system.nixos-generate-config) configuration desktopConfiguration; xserverEnabled = config.services.xserver.enable; @@ -177,6 +177,10 @@ in # users.users.jane = { # isNormalUser = true; # extraGroups = [ "wheel" ]; # Enable ‘sudo’ for the user. + # packages = with pkgs; [ + # firefox + # thunderbird + # ]; # }; # List packages installed in system profile. To search, run: @@ -184,7 +188,6 @@ in # environment.systemPackages = with pkgs; [ # vim # Do not forget to add an editor to edit configuration.nix! The Nano editor is also installed by default. # wget - # firefox # ]; # Some programs need SUID wrappers, can be configured further or are diff --git a/nixos/modules/misc/mandoc.nix b/nixos/modules/misc/mandoc.nix index 3da60f2f8e65f..838f20876563a 100644 --- a/nixos/modules/misc/mandoc.nix +++ b/nixos/modules/misc/mandoc.nix @@ -53,7 +53,9 @@ in { # see: https://inbox.vuxu.org/mandoc-tech/20210906171231.GF83680@athene.usta.de/T/#e85f773c1781e3fef85562b2794f9cad7b2909a3c extraSetup = lib.mkIf config.documentation.man.generateCaches '' ${makewhatis} -T utf8 ${ - lib.concatMapStringsSep " " (path: "\"$out/${path}\"") cfg.manPath + lib.concatMapStringsSep " " (path: + "$out/" + lib.escapeShellArg path + ) cfg.manPath } ''; }; diff --git a/nixos/modules/misc/version.nix b/nixos/modules/misc/version.nix index 931201ade2935..010acdb72f678 100644 --- a/nixos/modules/misc/version.nix +++ b/nixos/modules/misc/version.nix @@ -146,6 +146,15 @@ in "/etc/os-release".source = initrdRelease; "/etc/initrd-release".source = initrdRelease; }; + + # We have to use `warnings` because when warning in the default of the option + # the warning would also be shown when building the manual since the manual + # has to evaluate the default. + # + # TODO Remove this and drop the default of the option so people are forced to set it. + # Doing this also means fixing the comment in nixos/modules/testing/test-instrumentation.nix + warnings = lib.optional (options.system.stateVersion.highestPrio == (lib.mkOptionDefault { }).priority) + "system.stateVersion is not set, defaulting to ${config.system.stateVersion}. Read why this matters on https://nixos.org/manual/nixos/stable/options.html#opt-system.stateVersion."; }; # uses version info nixpkgs, which requires a full nixpkgs path diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index d1cda0d84e96b..84309f90132f1 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -57,6 +57,7 @@ ./hardware/sensor/hddtemp.nix ./hardware/sensor/iio.nix ./hardware/keyboard/teck.nix + ./hardware/keyboard/uhk.nix ./hardware/keyboard/zsa.nix ./hardware/ksm.nix ./hardware/ledger.nix @@ -196,7 +197,6 @@ ./programs/partition-manager.nix ./programs/plotinus.nix ./programs/proxychains.nix - ./programs/phosh.nix ./programs/qt5ct.nix ./programs/screen.nix ./programs/sedutil.nix @@ -505,6 +505,7 @@ ./services/mail/postfixadmin.nix ./services/mail/postsrsd.nix ./services/mail/postgrey.nix + ./services/mail/public-inbox.nix ./services/mail/spamassassin.nix ./services/mail/rspamd.nix ./services/mail/rss2email.nix @@ -975,6 +976,7 @@ ./services/security/hockeypuck.nix ./services/security/hologram-server.nix ./services/security/hologram-agent.nix + ./services/security/kanidm.nix ./services/security/munge.nix ./services/security/nginx-sso.nix ./services/security/oauth2_proxy.nix @@ -1078,6 +1080,7 @@ ./services/web-apps/trilium.nix ./services/web-apps/selfoss.nix ./services/web-apps/shiori.nix + ./services/web-apps/snipe-it.nix ./services/web-apps/vikunja.nix ./services/web-apps/virtlyst.nix ./services/web-apps/wiki-js.nix @@ -1256,6 +1259,7 @@ ./virtualisation/virtualbox-guest.nix ./virtualisation/virtualbox-host.nix ./virtualisation/vmware-guest.nix + ./virtualisation/vmware-host.nix ./virtualisation/waydroid.nix ./virtualisation/xen-dom0.nix ./virtualisation/xe-guest-utilities.nix diff --git a/nixos/modules/programs/thefuck.nix b/nixos/modules/programs/thefuck.nix index b909916158d38..18d09e26866c1 100644 --- a/nixos/modules/programs/thefuck.nix +++ b/nixos/modules/programs/thefuck.nix @@ -6,9 +6,12 @@ let prg = config.programs; cfg = prg.thefuck; - initScript = '' + bashAndZshInitScript = '' eval $(${pkgs.thefuck}/bin/thefuck --alias ${cfg.alias}) ''; + fishInitScript = '' + ${pkgs.thefuck}/bin/thefuck --alias ${cfg.alias} | source + ''; in { options = { @@ -30,10 +33,8 @@ in config = mkIf cfg.enable { environment.systemPackages = with pkgs; [ thefuck ]; - programs.bash.interactiveShellInit = initScript; - programs.zsh.interactiveShellInit = mkIf prg.zsh.enable initScript; - programs.fish.interactiveShellInit = mkIf prg.fish.enable '' - ${pkgs.thefuck}/bin/thefuck --alias | source - ''; + programs.bash.interactiveShellInit = bashAndZshInitScript; + programs.zsh.interactiveShellInit = mkIf prg.zsh.enable bashAndZshInitScript; + programs.fish.interactiveShellInit = mkIf prg.fish.enable fishInitScript; }; } diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix index 530304b497ae8..23d1344a57ac5 100644 --- a/nixos/modules/security/pam.nix +++ b/nixos/modules/security/pam.nix @@ -492,7 +492,7 @@ let auth ${ussh.control} ${pkgs.pam_ussh}/lib/security/pam_ussh.so ${optionalString (ussh.caFile != null) "ca_file=${ussh.caFile}"} ${optionalString (ussh.authorizedPrincipals != null) "authorized_principals=${ussh.authorizedPrincipals}"} ${optionalString (ussh.authorizedPrincipalsFile != null) "authorized_principals_file=${ussh.authorizedPrincipalsFile}"} ${optionalString (ussh.group != null) "group=${ussh.group}"} '') + (let oath = config.security.pam.oath; in optionalString cfg.oathAuth '' - auth requisite ${pkgs.oathToolkit}/lib/security/pam_oath.so window=${toString oath.window} usersfile=${toString oath.usersFile} digits=${toString oath.digits} + auth requisite ${pkgs.oath-toolkit}/lib/security/pam_oath.so window=${toString oath.window} usersfile=${toString oath.usersFile} digits=${toString oath.digits} '') + (let yubi = config.security.pam.yubico; in optionalString cfg.yubicoAuth '' auth ${yubi.control} ${pkgs.yubico-pam}/lib/security/pam_yubico.so mode=${toString yubi.mode} ${optionalString (yubi.challengeResponsePath != null) "chalresp_path=${yubi.challengeResponsePath}"} ${optionalString (yubi.mode == "client") "id=${toString yubi.id}"} ${optionalString yubi.debug "debug"} @@ -626,7 +626,7 @@ let session optional ${pkgs.otpw}/lib/security/pam_otpw.so '' + optionalString cfg.startSession '' - session optional ${pkgs.systemd}/lib/security/pam_systemd.so + session optional ${config.systemd.package}/lib/security/pam_systemd.so '' + optionalString cfg.forwardXAuth '' session optional pam_xauth.so xauthpath=${pkgs.xorg.xauth}/bin/xauth systemuser=99 @@ -1131,7 +1131,7 @@ in ++ optional config.services.sssd.enable pkgs.sssd ++ optionals config.krb5.enable [pam_krb5 pam_ccreds] ++ optionals config.security.pam.enableOTPW [ pkgs.otpw ] - ++ optionals config.security.pam.oath.enable [ pkgs.oathToolkit ] + ++ optionals config.security.pam.oath.enable [ pkgs.oath-toolkit ] ++ optionals config.security.pam.p11.enable [ pkgs.pam_p11 ] ++ optionals config.security.pam.u2f.enable [ pkgs.pam_u2f ]; @@ -1221,7 +1221,7 @@ in mr ${pkgs.pam_ussh}/lib/security/pam_ussh.so, '' + optionalString (isEnabled (cfg: cfg.oathAuth)) '' - "mr ${pkgs.oathToolkit}/lib/security/pam_oath.so, + "mr ${pkgs.oath-toolkit}/lib/security/pam_oath.so, '' + optionalString (isEnabled (cfg: cfg.yubicoAuth)) '' mr ${pkgs.yubico-pam}/lib/security/pam_yubico.so, @@ -1242,7 +1242,7 @@ in mr ${pkgs.gnome3.gnome-keyring}/lib/security/pam_gnome_keyring.so, '' + optionalString (isEnabled (cfg: cfg.startSession)) '' - mr ${pkgs.systemd}/lib/security/pam_systemd.so, + mr ${config.systemd.package}/lib/security/pam_systemd.so, '' + optionalString (isEnabled (cfg: cfg.enableAppArmor) && config.security.apparmor.enable) '' diff --git a/nixos/modules/security/wrappers/default.nix b/nixos/modules/security/wrappers/default.nix index e63f19010de8a..169ef7442626e 100644 --- a/nixos/modules/security/wrappers/default.nix +++ b/nixos/modules/security/wrappers/default.nix @@ -98,7 +98,7 @@ let # Prevent races chmod 0000 "$wrapperDir/${program}" - chown ${owner}.${group} "$wrapperDir/${program}" + chown ${owner}:${group} "$wrapperDir/${program}" # Set desired capabilities on the file plus cap_setpcap so # the wrapper program can elevate the capabilities set on @@ -126,7 +126,7 @@ let # Prevent races chmod 0000 "$wrapperDir/${program}" - chown ${owner}.${group} "$wrapperDir/${program}" + chown ${owner}:${group} "$wrapperDir/${program}" chmod "u${if setuid then "+" else "-"}s,g${if setgid then "+" else "-"}s,${permissions}" "$wrapperDir/${program}" ''; diff --git a/nixos/modules/services/backup/automysqlbackup.nix b/nixos/modules/services/backup/automysqlbackup.nix index fd2764a40ad2f..cf0cb4da32cf1 100644 --- a/nixos/modules/services/backup/automysqlbackup.nix +++ b/nixos/modules/services/backup/automysqlbackup.nix @@ -112,7 +112,7 @@ in services.mysql.ensureUsers = optional (config.services.mysql.enable && cfg.config.mysql_dump_host == "localhost") { name = user; - ensurePermissions = { "*.*" = "SELECT, SHOW VIEW, TRIGGER, LOCK TABLES"; }; + ensurePermissions = { "*.*" = "SELECT, SHOW VIEW, TRIGGER, LOCK TABLES, EVENT"; }; }; }; diff --git a/nixos/modules/services/backup/borgmatic.nix b/nixos/modules/services/backup/borgmatic.nix index 5e5c0bbecccaa..9414d78aa751d 100644 --- a/nixos/modules/services/backup/borgmatic.nix +++ b/nixos/modules/services/backup/borgmatic.nix @@ -4,7 +4,8 @@ with lib; let cfg = config.services.borgmatic; - cfgfile = pkgs.writeText "config.yaml" (builtins.toJSON cfg.settings); + settingsFormat = pkgs.formats.yaml { }; + cfgfile = settingsFormat.generate "config.yaml" cfg.settings; in { options.services.borgmatic = { enable = mkEnableOption "borgmatic"; @@ -14,7 +15,7 @@ in { See https://torsion.org/borgmatic/docs/reference/configuration/ ''; type = types.submodule { - freeformType = with lib.types; attrsOf anything; + freeformType = settingsFormat.type; options.location = { source_directories = mkOption { type = types.listOf types.str; diff --git a/nixos/modules/services/continuous-integration/hydra/default.nix b/nixos/modules/services/continuous-integration/hydra/default.nix index cc5de97d6d10d..90adab7fbf2e8 100644 --- a/nixos/modules/services/continuous-integration/hydra/default.nix +++ b/nixos/modules/services/continuous-integration/hydra/default.nix @@ -300,17 +300,17 @@ in }; preStart = '' mkdir -p ${baseDir} - chown hydra.hydra ${baseDir} + chown hydra:hydra ${baseDir} chmod 0750 ${baseDir} ln -sf ${hydraConf} ${baseDir}/hydra.conf mkdir -m 0700 -p ${baseDir}/www - chown hydra-www.hydra ${baseDir}/www + chown hydra-www:hydra ${baseDir}/www mkdir -m 0700 -p ${baseDir}/queue-runner mkdir -m 0750 -p ${baseDir}/build-logs - chown hydra-queue-runner.hydra ${baseDir}/queue-runner ${baseDir}/build-logs + chown hydra-queue-runner:hydra ${baseDir}/queue-runner ${baseDir}/build-logs ${optionalString haveLocalDB '' if ! [ -e ${baseDir}/.db-created ]; then @@ -338,7 +338,7 @@ in rmdir /nix/var/nix/gcroots/per-user/hydra-www/hydra-roots fi - chown hydra.hydra ${cfg.gcRootsDir} + chown hydra:hydra ${cfg.gcRootsDir} chmod 2775 ${cfg.gcRootsDir} ''; serviceConfig.ExecStart = "${hydra-package}/bin/hydra-init"; diff --git a/nixos/modules/services/games/factorio.nix b/nixos/modules/services/games/factorio.nix index ff73d7a46ed39..bb6898a08c52b 100644 --- a/nixos/modules/services/games/factorio.nix +++ b/nixos/modules/services/games/factorio.nix @@ -87,6 +87,18 @@ in a new map with default settings will be generated before starting the service. ''; }; + loadLatestSave = mkOption { + type = types.bool; + default = false; + description = '' + Load the latest savegame on startup. This overrides saveName, in that the latest + save will always be used even if a saved game of the given name exists. It still + controls the 'canonical' name of the savegame. + + Set this to true to have the server automatically reload a recent autosave after + a crash or desync. + ''; + }; # TODO Add more individual settings as nixos-options? # TODO XXX The server tries to copy a newly created config file over the old one # on shutdown, but fails, because it's in the nix store. When is this needed? @@ -250,8 +262,9 @@ in "--config=${cfg.configFile}" "--port=${toString cfg.port}" "--bind=${cfg.bind}" - "--start-server=${mkSavePath cfg.saveName}" + (optionalString (!cfg.loadLatestSave) "--start-server=${mkSavePath cfg.saveName}") "--server-settings=${serverSettingsFile}" + (optionalString cfg.loadLatestSave "--start-server-load-latest") (optionalString (cfg.mods != []) "--mod-directory=${modDir}") (optionalString (cfg.admins != []) "--server-adminlist=${serverAdminsFile}") ]; diff --git a/nixos/modules/services/hardware/illum.nix b/nixos/modules/services/hardware/illum.nix index ff73c99a65376..7f7a850002342 100644 --- a/nixos/modules/services/hardware/illum.nix +++ b/nixos/modules/services/hardware/illum.nix @@ -28,6 +28,7 @@ in { description = "Backlight Adjustment Service"; wantedBy = [ "multi-user.target" ]; serviceConfig.ExecStart = "${pkgs.illum}/bin/illum-d"; + serviceConfig.Restart = "on-failure"; }; }; diff --git a/nixos/modules/services/hardware/udev.nix b/nixos/modules/services/hardware/udev.nix index 8257eeb673b92..2f38661718738 100644 --- a/nixos/modules/services/hardware/udev.nix +++ b/nixos/modules/services/hardware/udev.nix @@ -8,6 +8,24 @@ let cfg = config.services.udev; + initrdUdevRules = pkgs.runCommand "initrd-udev-rules" {} '' + mkdir -p $out/etc/udev/rules.d + for f in 60-cdrom_id 60-persistent-storage 75-net-description 80-drivers 80-net-setup-link; do + ln -s ${config.boot.initrd.systemd.package}/lib/udev/rules.d/$f.rules $out/etc/udev/rules.d + done + ''; + + + # networkd link files are used early by udev to set up interfaces early. + # This must be done in stage 1 to avoid race conditions between udev and + # network daemons. + # TODO move this into the initrd-network module when it exists + initrdLinkUnits = pkgs.runCommand "initrd-link-units" {} '' + mkdir -p $out + ln -s ${udev}/lib/systemd/network/*.link $out/ + ${lib.concatMapStringsSep "\n" (file: "ln -s ${file} $out/") (lib.mapAttrsToList (n: v: "${v.unit}/${n}") (lib.filterAttrs (n: _: hasSuffix ".link" n) config.systemd.network.units))} + ''; + extraUdevRules = pkgs.writeTextFile { name = "extra-udev-rules"; text = cfg.extraRules; @@ -350,7 +368,10 @@ in ]; boot.initrd.systemd.storePaths = [ "${config.boot.initrd.systemd.package}/lib/systemd/systemd-udevd" - "${config.boot.initrd.systemd.package}/lib/udev" + "${config.boot.initrd.systemd.package}/lib/udev/ata_id" + "${config.boot.initrd.systemd.package}/lib/udev/cdrom_id" + "${config.boot.initrd.systemd.package}/lib/udev/scsi_id" + "${config.boot.initrd.systemd.package}/lib/udev/rules.d" ] ++ map (x: "${x}/bin") config.boot.initrd.services.udev.binPackages; # Generate the udev rules for the initrd @@ -364,13 +385,17 @@ in systemd = config.boot.initrd.systemd.package; binPackages = config.boot.initrd.services.udev.binPackages ++ [ config.boot.initrd.systemd.contents."/bin".source ]; }; + "/etc/systemd/network".source = initrdLinkUnits; }; - # Insert custom rules - boot.initrd.services.udev.packages = mkIf (config.boot.initrd.services.udev.rules != "") (pkgs.writeTextFile { - name = "initrd-udev-rules"; - destination = "/etc/udev/rules.d/99-local.rules"; - text = config.boot.initrd.services.udev.rules; - }); + # Insert initrd rules + boot.initrd.services.udev.packages = [ + initrdUdevRules + (mkIf (config.boot.initrd.services.udev.rules != "") (pkgs.writeTextFile { + name = "initrd-udev-rules"; + destination = "/etc/udev/rules.d/99-local.rules"; + text = config.boot.initrd.services.udev.rules; + })) + ]; environment.etc = { diff --git a/nixos/modules/services/hardware/usbrelayd.nix b/nixos/modules/services/hardware/usbrelayd.nix index c0322e89e6b12..2cee4e1ff7e8c 100644 --- a/nixos/modules/services/hardware/usbrelayd.nix +++ b/nixos/modules/services/hardware/usbrelayd.nix @@ -26,8 +26,7 @@ in config = mkIf cfg.enable { - # TODO: Rename to .conf in upcomming release - environment.etc."usbrelayd.ini".text = '' + environment.etc."usbrelayd.conf".text = '' [MQTT] BROKER = ${cfg.broker} CLIENTNAME = ${cfg.clientName} @@ -41,4 +40,8 @@ in }; users.groups.usbrelay = { }; }; + + meta = { + maintainers = with lib.maintainers; [ wentasah ]; + }; } diff --git a/nixos/modules/services/home-automation/home-assistant.nix b/nixos/modules/services/home-automation/home-assistant.nix index 6022227f6ea88..e255e5d22188b 100644 --- a/nixos/modules/services/home-automation/home-assistant.nix +++ b/nixos/modules/services/home-automation/home-assistant.nix @@ -360,7 +360,14 @@ in { }; config = mkIf cfg.enable { - networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.port ]; + assertions = [ + { + assertion = cfg.openFirewall -> !isNull cfg.config; + message = "openFirewall can only be used with a declarative config"; + } + ]; + + networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.config.http.server_port ]; systemd.services.home-assistant = { description = "Home Assistant"; diff --git a/nixos/modules/services/logging/logstash.nix b/nixos/modules/services/logging/logstash.nix index a08203dffe789..5d00feabe1c1d 100644 --- a/nixos/modules/services/logging/logstash.nix +++ b/nixos/modules/services/logging/logstash.nix @@ -109,7 +109,7 @@ in ''' # Read from journal pipe { - command => "''${pkgs.systemd}/bin/journalctl -f -o json" + command => "''${config.systemd.package}/bin/journalctl -f -o json" type => "syslog" codec => json {} } ''' diff --git a/nixos/modules/services/mail/public-inbox.nix b/nixos/modules/services/mail/public-inbox.nix new file mode 100644 index 0000000000000..0f9bc4ef226d2 --- /dev/null +++ b/nixos/modules/services/mail/public-inbox.nix @@ -0,0 +1,579 @@ +{ lib, pkgs, config, ... }: + +with lib; + +let + cfg = config.services.public-inbox; + stateDir = "/var/lib/public-inbox"; + + manref = name: vol: "<citerefentry><refentrytitle>${name}</refentrytitle><manvolnum>${toString vol}</manvolnum></citerefentry>"; + + gitIni = pkgs.formats.gitIni { listsAsDuplicateKeys = true; }; + iniAtom = elemAt gitIni.type/*attrsOf*/.functor.wrapped/*attrsOf*/.functor.wrapped/*either*/.functor.wrapped 0; + + useSpamAssassin = cfg.settings.publicinboxmda.spamcheck == "spamc" || + cfg.settings.publicinboxwatch.spamcheck == "spamc"; + + publicInboxDaemonOptions = proto: defaultPort: { + args = mkOption { + type = with types; listOf str; + default = []; + description = "Command-line arguments to pass to ${manref "public-inbox-${proto}d" 1}."; + }; + port = mkOption { + type = with types; nullOr (either str port); + default = defaultPort; + description = '' + Listening port. + Beware that public-inbox uses well-known ports number to decide whether to enable TLS or not. + Set to null and use <code>systemd.sockets.public-inbox-${proto}d.listenStreams</code> + if you need a more advanced listening. + ''; + }; + cert = mkOption { + type = with types; nullOr str; + default = null; + example = "/path/to/fullchain.pem"; + description = "Path to TLS certificate to use for connections to ${manref "public-inbox-${proto}d" 1}."; + }; + key = mkOption { + type = with types; nullOr str; + default = null; + example = "/path/to/key.pem"; + description = "Path to TLS key to use for connections to ${manref "public-inbox-${proto}d" 1}."; + }; + }; + + serviceConfig = srv: + let proto = removeSuffix "d" srv; + needNetwork = builtins.hasAttr proto cfg && cfg.${proto}.port == null; + in { + serviceConfig = { + # Enable JIT-compiled C (via Inline::C) + Environment = [ "PERL_INLINE_DIRECTORY=/run/public-inbox-${srv}/perl-inline" ]; + # NonBlocking is REQUIRED to avoid a race condition + # if running simultaneous services. + NonBlocking = true; + #LimitNOFILE = 30000; + User = config.users.users."public-inbox".name; + Group = config.users.groups."public-inbox".name; + RuntimeDirectory = [ + "public-inbox-${srv}/perl-inline" + ]; + RuntimeDirectoryMode = "700"; + # This is for BindPaths= and BindReadOnlyPaths= + # to allow traversal of directories they create inside RootDirectory= + UMask = "0066"; + StateDirectory = ["public-inbox"]; + StateDirectoryMode = "0750"; + WorkingDirectory = stateDir; + BindReadOnlyPaths = [ + "/etc" + "/run/systemd" + "${config.i18n.glibcLocales}" + ] ++ + mapAttrsToList (name: inbox: inbox.description) cfg.inboxes ++ + # Without confinement the whole Nix store + # is made available to the service + optionals (!config.systemd.services."public-inbox-${srv}".confinement.enable) [ + "${pkgs.dash}/bin/dash:/bin/sh" + builtins.storeDir + ]; + # The following options are only for optimizing: + # systemd-analyze security public-inbox-'*' + AmbientCapabilities = ""; + CapabilityBoundingSet = ""; + # ProtectClock= adds DeviceAllow=char-rtc r + DeviceAllow = ""; + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateNetwork = mkDefault (!needNetwork); + ProcSubset = "pid"; + ProtectClock = true; + ProtectHome = mkDefault true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectProc = "invisible"; + #ProtectSystem = "strict"; + RemoveIPC = true; + RestrictAddressFamilies = [ "AF_UNIX" ] ++ + optionals needNetwork [ "AF_INET" "AF_INET6" ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallFilter = [ + "@system-service" + "~@aio" "~@chown" "~@keyring" "~@memlock" "~@resources" + # Not removing @setuid and @privileged because Inline::C needs them. + # Not removing @timer because git upload-pack needs it. + ]; + SystemCallArchitectures = "native"; + + # The following options are redundant when confinement is enabled + RootDirectory = "/var/empty"; + TemporaryFileSystem = "/"; + PrivateMounts = true; + MountAPIVFS = true; + PrivateDevices = true; + PrivateTmp = true; + PrivateUsers = true; + ProtectControlGroups = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + }; + confinement = { + # Until we agree upon doing it directly here in NixOS + # https://github.com/NixOS/nixpkgs/pull/104457#issuecomment-1115768447 + # let the user choose to enable the confinement with: + # systemd.services.public-inbox-httpd.confinement.enable = true; + # systemd.services.public-inbox-imapd.confinement.enable = true; + # systemd.services.public-inbox-init.confinement.enable = true; + # systemd.services.public-inbox-nntpd.confinement.enable = true; + #enable = true; + mode = "full-apivfs"; + # Inline::C needs a /bin/sh, and dash is enough + binSh = "${pkgs.dash}/bin/dash"; + packages = [ + pkgs.iana-etc + (getLib pkgs.nss) + pkgs.tzdata + ]; + }; + }; +in + +{ + options.services.public-inbox = { + enable = mkEnableOption "the public-inbox mail archiver"; + package = mkOption { + type = types.package; + default = pkgs.public-inbox; + defaultText = literalExpression "pkgs.public-inbox"; + description = "public-inbox package to use."; + }; + path = mkOption { + type = with types; listOf package; + default = []; + example = literalExpression "with pkgs; [ spamassassin ]"; + description = '' + Additional packages to place in the path of public-inbox-mda, + public-inbox-watch, etc. + ''; + }; + inboxes = mkOption { + description = '' + Inboxes to configure, where attribute names are inbox names. + ''; + default = {}; + type = types.attrsOf (types.submodule ({name, ...}: { + freeformType = types.attrsOf iniAtom; + options.inboxdir = mkOption { + type = types.str; + default = "${stateDir}/inboxes/${name}"; + description = "The absolute path to the directory which hosts the public-inbox."; + }; + options.address = mkOption { + type = with types; listOf str; + example = "example-discuss@example.org"; + description = "The email addresses of the public-inbox."; + }; + options.url = mkOption { + type = with types; nullOr str; + default = null; + example = "https://example.org/lists/example-discuss"; + description = "URL where this inbox can be accessed over HTTP."; + }; + options.description = mkOption { + type = types.str; + example = "user/dev discussion of public-inbox itself"; + description = "User-visible description for the repository."; + apply = pkgs.writeText "public-inbox-description-${name}"; + }; + options.newsgroup = mkOption { + type = with types; nullOr str; + default = null; + description = "NNTP group name for the inbox."; + }; + options.watch = mkOption { + type = with types; listOf str; + default = []; + description = "Paths for ${manref "public-inbox-watch" 1} to monitor for new mail."; + example = [ "maildir:/path/to/test.example.com.git" ]; + }; + options.watchheader = mkOption { + type = with types; nullOr str; + default = null; + example = "List-Id:<test@example.com>"; + description = '' + If specified, ${manref "public-inbox-watch" 1} will only process + mail containing a matching header. + ''; + }; + options.coderepo = mkOption { + type = (types.listOf (types.enum (attrNames cfg.settings.coderepo))) // { + description = "list of coderepo names"; + }; + default = []; + description = "Nicknames of a 'coderepo' section associated with the inbox."; + }; + })); + }; + imap = { + enable = mkEnableOption "the public-inbox IMAP server"; + } // publicInboxDaemonOptions "imap" 993; + http = { + enable = mkEnableOption "the public-inbox HTTP server"; + mounts = mkOption { + type = with types; listOf str; + default = [ "/" ]; + example = [ "/lists/archives" ]; + description = '' + Root paths or URLs that public-inbox will be served on. + If domain parts are present, only requests to those + domains will be accepted. + ''; + }; + args = (publicInboxDaemonOptions "http" 80).args; + port = mkOption { + type = with types; nullOr (either str port); + default = 80; + example = "/run/public-inbox-httpd.sock"; + description = '' + Listening port or systemd's ListenStream= entry + to be used as a reverse proxy, eg. in nginx: + <code>locations."/inbox".proxyPass = "http://unix:''${config.services.public-inbox.http.port}:/inbox";</code> + Set to null and use <code>systemd.sockets.public-inbox-httpd.listenStreams</code> + if you need a more advanced listening. + ''; + }; + }; + mda = { + enable = mkEnableOption "the public-inbox Mail Delivery Agent"; + args = mkOption { + type = with types; listOf str; + default = []; + description = "Command-line arguments to pass to ${manref "public-inbox-mda" 1}."; + }; + }; + postfix.enable = mkEnableOption "the integration into Postfix"; + nntp = { + enable = mkEnableOption "the public-inbox NNTP server"; + } // publicInboxDaemonOptions "nntp" 563; + spamAssassinRules = mkOption { + type = with types; nullOr path; + default = "${cfg.package.sa_config}/user/.spamassassin/user_prefs"; + defaultText = literalExpression "\${cfg.package.sa_config}/user/.spamassassin/user_prefs"; + description = "SpamAssassin configuration specific to public-inbox."; + }; + settings = mkOption { + description = '' + Settings for the <link xlink:href="https://public-inbox.org/public-inbox-config.html">public-inbox config file</link>. + ''; + default = {}; + type = types.submodule { + freeformType = gitIni.type; + options.publicinbox = mkOption { + default = {}; + description = "public inboxes"; + type = types.submodule { + freeformType = with types; /*inbox name*/attrsOf (/*inbox option name*/attrsOf /*inbox option value*/iniAtom); + options.css = mkOption { + type = with types; listOf str; + default = []; + description = "The local path name of a CSS file for the PSGI web interface."; + }; + options.nntpserver = mkOption { + type = with types; listOf str; + default = []; + example = [ "nntp://news.public-inbox.org" "nntps://news.public-inbox.org" ]; + description = "NNTP URLs to this public-inbox instance"; + }; + options.wwwlisting = mkOption { + type = with types; enum [ "all" "404" "match=domain" ]; + default = "404"; + description = '' + Controls which lists (if any) are listed for when the root + public-inbox URL is accessed over HTTP. + ''; + }; + }; + }; + options.publicinboxmda.spamcheck = mkOption { + type = with types; enum [ "spamc" "none" ]; + default = "none"; + description = '' + If set to spamc, ${manref "public-inbox-watch" 1} will filter spam + using SpamAssassin. + ''; + }; + options.publicinboxwatch.spamcheck = mkOption { + type = with types; enum [ "spamc" "none" ]; + default = "none"; + description = '' + If set to spamc, ${manref "public-inbox-watch" 1} will filter spam + using SpamAssassin. + ''; + }; + options.publicinboxwatch.watchspam = mkOption { + type = with types; nullOr str; + default = null; + example = "maildir:/path/to/spam"; + description = '' + If set, mail in this maildir will be trained as spam and + deleted from all watched inboxes + ''; + }; + options.coderepo = mkOption { + default = {}; + description = "code repositories"; + type = types.attrsOf (types.submodule { + freeformType = types.attrsOf iniAtom; + options.cgitUrl = mkOption { + type = types.str; + description = "URL of a cgit instance"; + }; + options.dir = mkOption { + type = types.str; + description = "Path to a git repository"; + }; + }); + }; + }; + }; + openFirewall = mkEnableOption "opening the firewall when using a port option"; + }; + config = mkIf cfg.enable { + assertions = [ + { assertion = config.services.spamassassin.enable || !useSpamAssassin; + message = '' + public-inbox is configured to use SpamAssassin, but + services.spamassassin.enable is false. If you don't need + spam checking, set `services.public-inbox.settings.publicinboxmda.spamcheck' and + `services.public-inbox.settings.publicinboxwatch.spamcheck' to null. + ''; + } + { assertion = cfg.path != [] || !useSpamAssassin; + message = '' + public-inbox is configured to use SpamAssassin, but there is + no spamc executable in services.public-inbox.path. If you + don't need spam checking, set + `services.public-inbox.settings.publicinboxmda.spamcheck' and + `services.public-inbox.settings.publicinboxwatch.spamcheck' to null. + ''; + } + ]; + services.public-inbox.settings = + filterAttrsRecursive (n: v: v != null) { + publicinbox = mapAttrs (n: filterAttrs (n: v: n != "description")) cfg.inboxes; + }; + users = { + users.public-inbox = { + home = stateDir; + group = "public-inbox"; + isSystemUser = true; + }; + groups.public-inbox = {}; + }; + networking.firewall = mkIf cfg.openFirewall + { allowedTCPPorts = mkMerge + (map (proto: (mkIf (cfg.${proto}.enable && types.port.check cfg.${proto}.port) [ cfg.${proto}.port ])) + ["imap" "http" "nntp"]); + }; + services.postfix = mkIf (cfg.postfix.enable && cfg.mda.enable) { + # Not sure limiting to 1 is necessary, but better safe than sorry. + config.public-inbox_destination_recipient_limit = "1"; + + # Register the addresses as existing + virtual = + concatStringsSep "\n" (mapAttrsToList (_: inbox: + concatMapStringsSep "\n" (address: + "${address} ${address}" + ) inbox.address + ) cfg.inboxes); + + # Deliver the addresses with the public-inbox transport + transport = + concatStringsSep "\n" (mapAttrsToList (_: inbox: + concatMapStringsSep "\n" (address: + "${address} public-inbox:${address}" + ) inbox.address + ) cfg.inboxes); + + # The public-inbox transport + masterConfig.public-inbox = { + type = "unix"; + privileged = true; # Required for user= + command = "pipe"; + args = [ + "flags=X" # Report as a final delivery + "user=${with config.users; users."public-inbox".name + ":" + groups."public-inbox".name}" + # Specifying a nexthop when using the transport + # (eg. test public-inbox:test) allows to + # receive mails with an extension (eg. test+foo). + "argv=${pkgs.writeShellScript "public-inbox-transport" '' + export HOME="${stateDir}" + export ORIGINAL_RECIPIENT="''${2:-1}" + export PATH="${makeBinPath cfg.path}:$PATH" + exec ${cfg.package}/bin/public-inbox-mda ${escapeShellArgs cfg.mda.args} + ''} \${original_recipient} \${nexthop}" + ]; + }; + }; + systemd.sockets = mkMerge (map (proto: + mkIf (cfg.${proto}.enable && cfg.${proto}.port != null) + { "public-inbox-${proto}d" = { + listenStreams = [ (toString cfg.${proto}.port) ]; + wantedBy = [ "sockets.target" ]; + }; + } + ) [ "imap" "http" "nntp" ]); + systemd.services = mkMerge [ + (mkIf cfg.imap.enable + { public-inbox-imapd = mkMerge [(serviceConfig "imapd") { + after = [ "public-inbox-init.service" "public-inbox-watch.service" ]; + requires = [ "public-inbox-init.service" ]; + serviceConfig = { + ExecStart = escapeShellArgs ( + [ "${cfg.package}/bin/public-inbox-imapd" ] ++ + cfg.imap.args ++ + optionals (cfg.imap.cert != null) [ "--cert" cfg.imap.cert ] ++ + optionals (cfg.imap.key != null) [ "--key" cfg.imap.key ] + ); + }; + }]; + }) + (mkIf cfg.http.enable + { public-inbox-httpd = mkMerge [(serviceConfig "httpd") { + after = [ "public-inbox-init.service" "public-inbox-watch.service" ]; + requires = [ "public-inbox-init.service" ]; + serviceConfig = { + ExecStart = escapeShellArgs ( + [ "${cfg.package}/bin/public-inbox-httpd" ] ++ + cfg.http.args ++ + # See https://public-inbox.org/public-inbox.git/tree/examples/public-inbox.psgi + # for upstream's example. + [ (pkgs.writeText "public-inbox.psgi" '' + #!${cfg.package.fullperl} -w + use strict; + use warnings; + use Plack::Builder; + use PublicInbox::WWW; + + my $www = PublicInbox::WWW->new; + $www->preload; + + builder { + # If reached through a reverse proxy, + # make it transparent by resetting some HTTP headers + # used by public-inbox to generate URIs. + enable 'ReverseProxy'; + + # No need to send a response body if it's an HTTP HEAD requests. + enable 'Head'; + + # Route according to configured domains and root paths. + ${concatMapStrings (path: '' + mount q(${path}) => sub { $www->call(@_); }; + '') cfg.http.mounts} + } + '') ] + ); + }; + }]; + }) + (mkIf cfg.nntp.enable + { public-inbox-nntpd = mkMerge [(serviceConfig "nntpd") { + after = [ "public-inbox-init.service" "public-inbox-watch.service" ]; + requires = [ "public-inbox-init.service" ]; + serviceConfig = { + ExecStart = escapeShellArgs ( + [ "${cfg.package}/bin/public-inbox-nntpd" ] ++ + cfg.nntp.args ++ + optionals (cfg.nntp.cert != null) [ "--cert" cfg.nntp.cert ] ++ + optionals (cfg.nntp.key != null) [ "--key" cfg.nntp.key ] + ); + }; + }]; + }) + (mkIf (any (inbox: inbox.watch != []) (attrValues cfg.inboxes) + || cfg.settings.publicinboxwatch.watchspam != null) + { public-inbox-watch = mkMerge [(serviceConfig "watch") { + inherit (cfg) path; + wants = [ "public-inbox-init.service" ]; + requires = [ "public-inbox-init.service" ] ++ + optional (cfg.settings.publicinboxwatch.spamcheck == "spamc") "spamassassin.service"; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + ExecStart = "${cfg.package}/bin/public-inbox-watch"; + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + }; + }]; + }) + ({ public-inbox-init = let + PI_CONFIG = gitIni.generate "public-inbox.ini" + (filterAttrsRecursive (n: v: v != null) cfg.settings); + in mkMerge [(serviceConfig "init") { + wantedBy = [ "multi-user.target" ]; + restartIfChanged = true; + restartTriggers = [ PI_CONFIG ]; + script = '' + set -ux + install -D -p ${PI_CONFIG} ${stateDir}/.public-inbox/config + '' + optionalString useSpamAssassin '' + install -m 0700 -o spamd -d ${stateDir}/.spamassassin + ${optionalString (cfg.spamAssassinRules != null) '' + ln -sf ${cfg.spamAssassinRules} ${stateDir}/.spamassassin/user_prefs + ''} + '' + concatStrings (mapAttrsToList (name: inbox: '' + if [ ! -e ${stateDir}/inboxes/${escapeShellArg name} ]; then + # public-inbox-init creates an inbox and adds it to a config file. + # It tries to atomically write the config file by creating + # another file in the same directory, and renaming it. + # This has the sad consequence that we can't use + # /dev/null, or it would try to create a file in /dev. + conf_dir="$(mktemp -d)" + + PI_CONFIG=$conf_dir/conf \ + ${cfg.package}/bin/public-inbox-init -V2 \ + ${escapeShellArgs ([ name "${stateDir}/inboxes/${name}" inbox.url ] ++ inbox.address)} + + rm -rf $conf_dir + fi + + ln -sf ${inbox.description} \ + ${stateDir}/inboxes/${escapeShellArg name}/description + + export GIT_DIR=${stateDir}/inboxes/${escapeShellArg name}/all.git + if test -d "$GIT_DIR"; then + # Config is inherited by each epoch repository, + # so just needs to be set for all.git. + ${pkgs.git}/bin/git config core.sharedRepository 0640 + fi + '') cfg.inboxes + ) + '' + shopt -s nullglob + for inbox in ${stateDir}/inboxes/*/; do + # This should be idempotent, but only do it for new + # inboxes anyway because it's only needed once, and could + # be slow for large pre-existing inboxes. + ls -1 "$inbox" | grep -q '^xap' || + ${cfg.package}/bin/public-inbox-index "$inbox" + done + ''; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + StateDirectory = [ + "public-inbox/.public-inbox" + "public-inbox/.public-inbox/emergency" + "public-inbox/inboxes" + ]; + }; + }]; + }) + ]; + environment.systemPackages = with pkgs; [ cfg.package ]; + }; + meta.maintainers = with lib.maintainers; [ julm qyliss ]; +} diff --git a/nixos/modules/services/mail/spamassassin.nix b/nixos/modules/services/mail/spamassassin.nix index ac878222b26a2..3b10d8d29090d 100644 --- a/nixos/modules/services/mail/spamassassin.nix +++ b/nixos/modules/services/mail/spamassassin.nix @@ -135,7 +135,7 @@ in User = "spamd"; Group = "spamd"; StateDirectory = "spamassassin"; - ExecStartPost = "+${pkgs.systemd}/bin/systemctl -q --no-block try-reload-or-restart spamd.service"; + ExecStartPost = "+${config.systemd.package}/bin/systemctl -q --no-block try-reload-or-restart spamd.service"; }; script = '' diff --git a/nixos/modules/services/matrix/matrix-synapse.nix b/nixos/modules/services/matrix/matrix-synapse.nix index a498aff7a55bb..87a977f8e1efb 100644 --- a/nixos/modules/services/matrix/matrix-synapse.nix +++ b/nixos/modules/services/matrix/matrix-synapse.nix @@ -296,6 +296,7 @@ in { default = if lib.versionAtLeast config.system.stateVersion "22.05" then "${cfg.dataDir}/media_store" else "${cfg.dataDir}/media"; + defaultText = "${cfg.dataDir}/media_store for when system.stateVersion is at least 22.05, ${cfg.dataDir}/media when lower than 22.05"; description = '' Directory where uploaded images and attachments are stored. ''; diff --git a/nixos/modules/services/misc/dendrite.nix b/nixos/modules/services/misc/dendrite.nix index 35bec40926ec6..ac5df9951b3f0 100644 --- a/nixos/modules/services/misc/dendrite.nix +++ b/nixos/modules/services/misc/dendrite.nix @@ -222,6 +222,13 @@ in for available options with which to populate settings. ''; }; + openRegistration = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + Allow open registration without secondary verification (reCAPTCHA). + ''; + }; }; config = lib.mkIf cfg.enable { @@ -263,6 +270,8 @@ in "--https-bind-address :${builtins.toString cfg.httpsPort}" "--tls-cert ${cfg.tlsCert}" "--tls-key ${cfg.tlsKey}" + ] ++ lib.optionals cfg.openRegistration [ + "--really-enable-open-registration" ]); ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; Restart = "on-failure"; diff --git a/nixos/modules/services/misc/heisenbridge.nix b/nixos/modules/services/misc/heisenbridge.nix index 7ce8a23d9af12..deefb061d8b31 100644 --- a/nixos/modules/services/misc/heisenbridge.nix +++ b/nixos/modules/services/misc/heisenbridge.nix @@ -204,7 +204,7 @@ in NoNewPrivileges = true; LockPersonality = true; RestrictRealtime = true; - SystemCallFilter = ["@system-service" "~@priviledged" "@chown"]; + SystemCallFilter = ["@system-service" "~@privileged" "@chown"]; SystemCallArchitectures = "native"; RestrictAddressFamilies = "AF_INET AF_INET6"; }; diff --git a/nixos/modules/services/monitoring/prometheus/default.nix b/nixos/modules/services/monitoring/prometheus/default.nix index 52525e8935bae..ceb2db1faefd8 100644 --- a/nixos/modules/services/monitoring/prometheus/default.nix +++ b/nixos/modules/services/monitoring/prometheus/default.nix @@ -3,6 +3,7 @@ with lib; let + json = pkgs.formats.json { }; cfg = config.services.prometheus; workingDir = "/var/lib/" + cfg.stateDir; @@ -34,13 +35,7 @@ let promtool ${what} $out '' else file; - # Pretty-print JSON to a file - writePrettyJSON = name: x: - pkgs.runCommandLocal name { } '' - echo '${builtins.toJSON x}' | ${pkgs.jq}/bin/jq . > $out - ''; - - generatedPrometheusYml = writePrettyJSON "prometheus.yml" promConfig; + generatedPrometheusYml = json.generate "prometheus.yml" promConfig; # This becomes the main config file for Prometheus promConfig = { diff --git a/nixos/modules/services/networking/gateone.nix b/nixos/modules/services/networking/gateone.nix index 3e3a3c1aa94d4..e68f8a47d5c0d 100644 --- a/nixos/modules/services/networking/gateone.nix +++ b/nixos/modules/services/networking/gateone.nix @@ -36,11 +36,11 @@ config = mkIf cfg.enable { preStart = '' if [ ! -d ${cfg.settingsDir} ] ; then mkdir -m 0750 -p ${cfg.settingsDir} - chown -R gateone.gateone ${cfg.settingsDir} + chown -R gateone:gateone ${cfg.settingsDir} fi if [ ! -d ${cfg.pidDir} ] ; then mkdir -m 0750 -p ${cfg.pidDir} - chown -R gateone.gateone ${cfg.pidDir} + chown -R gateone:gateone ${cfg.pidDir} fi ''; #unitConfig.RequiresMountsFor = "${cfg.settingsDir}"; diff --git a/nixos/modules/services/networking/ircd-hybrid/ircd.conf b/nixos/modules/services/networking/ircd-hybrid/ircd.conf index 17ef203840af5..b82094cf5f093 100644 --- a/nixos/modules/services/networking/ircd-hybrid/ircd.conf +++ b/nixos/modules/services/networking/ircd-hybrid/ircd.conf @@ -98,7 +98,7 @@ serverinfo { * * openssl genrsa -out rsa.key 2048 * openssl rsa -in rsa.key -pubout -out rsa.pub - * chown <ircd-user>.<ircd.group> rsa.key rsa.pub + * chown <ircd-user>:<ircd.group> rsa.key rsa.pub * chmod 0600 rsa.key * chmod 0644 rsa.pub */ diff --git a/nixos/modules/services/networking/pleroma.nix b/nixos/modules/services/networking/pleroma.nix index c6d4c14dcb7e2..9b8382392c0a7 100644 --- a/nixos/modules/services/networking/pleroma.nix +++ b/nixos/modules/services/networking/pleroma.nix @@ -1,7 +1,6 @@ { config, options, lib, pkgs, stdenv, ... }: let cfg = config.services.pleroma; - cookieFile = "/var/lib/pleroma/.cookie"; in { options = { services.pleroma = with lib; { @@ -9,7 +8,7 @@ in { package = mkOption { type = types.package; - default = pkgs.pleroma.override { inherit cookieFile; }; + default = pkgs.pleroma; defaultText = literalExpression "pkgs.pleroma"; description = "Pleroma package to use."; }; @@ -101,6 +100,7 @@ in { after = [ "network-online.target" "postgresql.service" ]; wantedBy = [ "multi-user.target" ]; restartTriggers = [ config.environment.etc."/pleroma/config.exs".source ]; + environment.RELEASE_COOKIE = "/var/lib/pleroma/.cookie"; serviceConfig = { User = cfg.user; Group = cfg.group; @@ -118,10 +118,10 @@ in { # Better be safe than sorry migration-wise. ExecStartPre = let preScript = pkgs.writers.writeBashBin "pleromaStartPre" '' - if [ ! -f "${cookieFile}" ] || [ ! -s "${cookieFile}" ] + if [ ! -f /var/lib/pleroma/.cookie ] then echo "Creating cookie file" - dd if=/dev/urandom bs=1 count=16 | ${pkgs.hexdump}/bin/hexdump -e '16/1 "%02x"' > "${cookieFile}" + dd if=/dev/urandom bs=1 count=16 | hexdump -e '16/1 "%02x"' > /var/lib/pleroma/.cookie fi ${cfg.package}/bin/pleroma_ctl migrate ''; diff --git a/nixos/modules/services/networking/pptpd.nix b/nixos/modules/services/networking/pptpd.nix index 3e7753b9dd352..423e14e998f85 100644 --- a/nixos/modules/services/networking/pptpd.nix +++ b/nixos/modules/services/networking/pptpd.nix @@ -108,7 +108,7 @@ with lib; #username pptpd password * EOF - chown root.root "$secrets" + chown root:root "$secrets" chmod 600 "$secrets" ''; diff --git a/nixos/modules/services/networking/prayer.nix b/nixos/modules/services/networking/prayer.nix index ae9258b27122f..513509eaca3ab 100644 --- a/nixos/modules/services/networking/prayer.nix +++ b/nixos/modules/services/networking/prayer.nix @@ -82,7 +82,7 @@ in serviceConfig.Type = "forking"; preStart = '' mkdir -m 0755 -p ${stateDir} - chown ${prayerUser}.${prayerGroup} ${stateDir} + chown ${prayerUser}:${prayerGroup} ${stateDir} ''; script = "${prayer}/sbin/prayer --config-file=${prayerCfg}"; }; diff --git a/nixos/modules/services/networking/ssh/sshd.nix b/nixos/modules/services/networking/ssh/sshd.nix index 230ab673a9761..d467c3c0471cf 100644 --- a/nixos/modules/services/networking/ssh/sshd.nix +++ b/nixos/modules/services/networking/ssh/sshd.nix @@ -441,6 +441,7 @@ in ${flip concatMapStrings cfg.hostKeys (k: '' if ! [ -s "${k.path}" ]; then + rm -f "${k.path}" ssh-keygen \ -t "${k.type}" \ ${if k ? bits then "-b ${toString k.bits}" else ""} \ diff --git a/nixos/modules/services/networking/supplicant.nix b/nixos/modules/services/networking/supplicant.nix index 8df450a11c633..e111b311d68f4 100644 --- a/nixos/modules/services/networking/supplicant.nix +++ b/nixos/modules/services/networking/supplicant.nix @@ -226,10 +226,10 @@ in ACTION=="add", SUBSYSTEM=="net", ENV{INTERFACE}=="${i}", TAG+="systemd", ENV{SYSTEMD_WANTS}+="supplicant-${replaceChars [" "] ["-"] iface}.service", TAG+="SUPPLICANT_ASSIGNED"''))} ${optionalString (hasAttr "WLAN" cfg) '' - ACTION=="add", SUBSYSTEM=="net", ENV{DEVTYPE}=="wlan", TAG!="SUPPLICANT_ASSIGNED", TAG+="systemd", PROGRAM="${pkgs.systemd}/bin/systemd-escape -p %E{INTERFACE}", ENV{SYSTEMD_WANTS}+="supplicant-wlan@$result.service" + ACTION=="add", SUBSYSTEM=="net", ENV{DEVTYPE}=="wlan", TAG!="SUPPLICANT_ASSIGNED", TAG+="systemd", PROGRAM="/run/current-system/systemd/bin/systemd-escape -p %E{INTERFACE}", ENV{SYSTEMD_WANTS}+="supplicant-wlan@$result.service" ''} ${optionalString (hasAttr "LAN" cfg) '' - ACTION=="add", SUBSYSTEM=="net", ENV{DEVTYPE}=="lan", TAG!="SUPPLICANT_ASSIGNED", TAG+="systemd", PROGRAM="${pkgs.systemd}/bin/systemd-escape -p %E{INTERFACE}", ENV{SYSTEMD_WANTS}+="supplicant-lan@$result.service" + ACTION=="add", SUBSYSTEM=="net", ENV{DEVTYPE}=="lan", TAG!="SUPPLICANT_ASSIGNED", TAG+="systemd", PROGRAM="/run/current-system/systemd/bin/systemd-escape -p %E{INTERFACE}", ENV{SYSTEMD_WANTS}+="supplicant-lan@$result.service" ''} ''; })]; diff --git a/nixos/modules/services/networking/tailscale.nix b/nixos/modules/services/networking/tailscale.nix index 1f64113950a79..0133874d0e0d0 100644 --- a/nixos/modules/services/networking/tailscale.nix +++ b/nixos/modules/services/networking/tailscale.nix @@ -2,9 +2,13 @@ with lib; -let cfg = config.services.tailscale; +let + cfg = config.services.tailscale; + firewallOn = config.networking.firewall.enable; + rpfMode = config.networking.firewall.checkReversePath; + rpfIsStrict = rpfMode == true || rpfMode == "strict"; in { - meta.maintainers = with maintainers; [ danderson mbaillie ]; + meta.maintainers = with maintainers; [ danderson mbaillie twitchyliquid64 ]; options.services.tailscale = { enable = mkEnableOption "Tailscale client daemon"; @@ -36,17 +40,34 @@ in { }; config = mkIf cfg.enable { + warnings = optional (firewallOn && rpfIsStrict) "Strict reverse path filtering breaks Tailscale exit node use and some subnet routing setups. Consider setting `networking.firewall.checkReversePath` = 'loose'"; environment.systemPackages = [ cfg.package ]; # for the CLI systemd.packages = [ cfg.package ]; systemd.services.tailscaled = { wantedBy = [ "multi-user.target" ]; - path = [ pkgs.openresolv pkgs.procps ]; + path = [ + pkgs.openresolv # for configuring DNS in some configs + pkgs.procps # for collecting running services (opt-in feature) + pkgs.glibc # for `getent` to look up user shells + ]; serviceConfig.Environment = [ "PORT=${toString cfg.port}" ''"FLAGS=--tun ${lib.escapeShellArg cfg.interfaceName}"'' ] ++ (lib.optionals (cfg.permitCertUid != null) [ "TS_PERMIT_CERT_UID=${cfg.permitCertUid}" ]); + # Restart tailscaled with a single `systemctl restart` at the + # end of activation, rather than a `stop` followed by a later + # `start`. Activation over Tailscale can hang for tens of + # seconds in the stop+start setup, if the activation script has + # a significant delay between the stop and start phases + # (e.g. script blocked on another unit with a slow shutdown). + # + # Tailscale is aware of the correctness tradeoff involved, and + # already makes its upstream systemd unit robust against unit + # version mismatches on restart for compatibility with other + # linux distros. + stopIfChanged = false; }; }; } diff --git a/nixos/modules/services/networking/wg-quick.nix b/nixos/modules/services/networking/wg-quick.nix index 61e9fe5096b1f..0b3815d0cc628 100644 --- a/nixos/modules/services/networking/wg-quick.nix +++ b/nixos/modules/services/networking/wg-quick.nix @@ -211,7 +211,7 @@ let postUp = optional (values.privateKeyFile != null) "wg set ${name} private-key <(cat ${values.privateKeyFile})" ++ (concatMap (peer: optional (peer.presharedKeyFile != null) "wg set ${name} peer ${peer.publicKey} preshared-key <(cat ${peer.presharedKeyFile})") values.peers) ++ - optional (values.postUp != null) values.postUp; + optional (values.postUp != "") values.postUp; postUpFile = if postUp != [] then writeScriptFile "postUp.sh" (concatMapStringsSep "\n" (line: line) postUp) else null; preDownFile = if values.preDown != "" then writeScriptFile "preDown.sh" values.preDown else null; postDownFile = if values.postDown != "" then writeScriptFile "postDown.sh" values.postDown else null; diff --git a/nixos/modules/services/networking/xl2tpd.nix b/nixos/modules/services/networking/xl2tpd.nix index 7dbe51422d964..9418488c1e94f 100644 --- a/nixos/modules/services/networking/xl2tpd.nix +++ b/nixos/modules/services/networking/xl2tpd.nix @@ -116,18 +116,18 @@ with lib; #username xl2tpd password * EOF - chown root.root ppp/chap-secrets + chown root:root ppp/chap-secrets chmod 600 ppp/chap-secrets # The documentation says this file should be present but doesn't explain why and things work even if not there: [ -f l2tp-secrets ] || (echo -n "* * "; ${pkgs.apg}/bin/apg -n 1 -m 32 -x 32 -a 1 -M LCN) > l2tp-secrets - chown root.root l2tp-secrets + chown root:root l2tp-secrets chmod 600 l2tp-secrets popd > /dev/null mkdir -p /run/xl2tpd - chown root.root /run/xl2tpd + chown root:root /run/xl2tpd chmod 700 /run/xl2tpd ''; diff --git a/nixos/modules/services/security/kanidm.nix b/nixos/modules/services/security/kanidm.nix new file mode 100644 index 0000000000000..a7c51b9a877f1 --- /dev/null +++ b/nixos/modules/services/security/kanidm.nix @@ -0,0 +1,345 @@ +{ config, lib, options, pkgs, ... }: +let + cfg = config.services.kanidm; + settingsFormat = pkgs.formats.toml { }; + # Remove null values, so we can document optional values that don't end up in the generated TOML file. + filterConfig = lib.converge (lib.filterAttrsRecursive (_: v: v != null)); + serverConfigFile = settingsFormat.generate "server.toml" (filterConfig cfg.serverSettings); + clientConfigFile = settingsFormat.generate "kanidm-config.toml" (filterConfig cfg.clientSettings); + unixConfigFile = settingsFormat.generate "kanidm-unixd.toml" (filterConfig cfg.unixSettings); + + defaultServiceConfig = { + BindReadOnlyPaths = [ + "/nix/store" + "-/etc/resolv.conf" + "-/etc/nsswitch.conf" + "-/etc/hosts" + "-/etc/localtime" + ]; + CapabilityBoundingSet = ""; + # ProtectClock= adds DeviceAllow=char-rtc r + DeviceAllow = ""; + # Implies ProtectSystem=strict, which re-mounts all paths + # DynamicUser = true; + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateMounts = true; + PrivateNetwork = true; + PrivateTmp = true; + PrivateUsers = true; + ProcSubset = "pid"; + ProtectClock = true; + ProtectHome = true; + ProtectHostname = true; + # Would re-mount paths ignored by temporary root + #ProtectSystem = "strict"; + ProtectControlGroups = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + RestrictAddressFamilies = [ ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + SystemCallFilter = [ "@system-service" "~@privileged @resources @setuid @keyring" ]; + # Does not work well with the temporary root + #UMask = "0066"; + }; + +in +{ + options.services.kanidm = { + enableClient = lib.mkEnableOption "the Kanidm client"; + enableServer = lib.mkEnableOption "the Kanidm server"; + enablePam = lib.mkEnableOption "the Kanidm PAM and NSS integration."; + + serverSettings = lib.mkOption { + type = lib.types.submodule { + freeformType = settingsFormat.type; + + options = { + bindaddress = lib.mkOption { + description = "Address/port combination the webserver binds to."; + example = "[::1]:8443"; + type = lib.types.str; + }; + # Should be optional but toml does not accept null + ldapbindaddress = lib.mkOption { + description = '' + Address and port the LDAP server is bound to. Setting this to <literal>null</literal> disables the LDAP interface. + ''; + example = "[::1]:636"; + default = null; + type = lib.types.nullOr lib.types.str; + }; + origin = lib.mkOption { + description = "The origin of your Kanidm instance. Must have https as protocol."; + example = "https://idm.example.org"; + type = lib.types.strMatching "^https://.*"; + }; + domain = lib.mkOption { + description = '' + The <literal>domain</literal> that Kanidm manages. Must be below or equal to the domain + specified in <literal>serverSettings.origin</literal>. + This can be left at <literal>null</literal>, only if your instance has the role <literal>ReadOnlyReplica</literal>. + While it is possible to change the domain later on, it requires extra steps! + Please consider the warnings and execute the steps described + <link xlink:href="https://kanidm.github.io/kanidm/stable/administrivia.html#rename-the-domain">in the documentation</link>. + ''; + example = "example.org"; + default = null; + type = lib.types.nullOr lib.types.str; + }; + db_path = lib.mkOption { + description = "Path to Kanidm database."; + default = "/var/lib/kanidm/kanidm.db"; + readOnly = true; + type = lib.types.path; + }; + log_level = lib.mkOption { + description = "Log level of the server."; + default = "default"; + type = lib.types.enum [ "default" "verbose" "perfbasic" "perffull" ]; + }; + role = lib.mkOption { + description = "The role of this server. This affects the replication relationship and thereby available features."; + default = "WriteReplica"; + type = lib.types.enum [ "WriteReplica" "WriteReplicaNoUI" "ReadOnlyReplica" ]; + }; + }; + }; + default = { }; + description = '' + Settings for Kanidm, see + <link xlink:href="https://github.com/kanidm/kanidm/blob/master/kanidm_book/src/server_configuration.md">the documentation</link> + and <link xlink:href="https://github.com/kanidm/kanidm/blob/master/examples/server.toml">example configuration</link> + for possible values. + ''; + }; + + clientSettings = lib.mkOption { + type = lib.types.submodule { + freeformType = settingsFormat.type; + + options.uri = lib.mkOption { + description = "Address of the Kanidm server."; + example = "http://127.0.0.1:8080"; + type = lib.types.str; + }; + }; + description = '' + Configure Kanidm clients, needed for the PAM daemon. See + <link xlink:href="https://github.com/kanidm/kanidm/blob/master/kanidm_book/src/client_tools.md#kanidm-configuration">the documentation</link> + and <link xlink:href="https://github.com/kanidm/kanidm/blob/master/examples/config">example configuration</link> + for possible values. + ''; + }; + + unixSettings = lib.mkOption { + type = lib.types.submodule { + freeformType = settingsFormat.type; + + options.pam_allowed_login_groups = lib.mkOption { + description = "Kanidm groups that are allowed to login using PAM."; + example = "my_pam_group"; + type = lib.types.listOf lib.types.str; + }; + }; + description = '' + Configure Kanidm unix daemon. + See <link xlink:href="https://github.com/kanidm/kanidm/blob/master/kanidm_book/src/pam_and_nsswitch.md#the-unix-daemon">the documentation</link> + and <link xlink:href="https://github.com/kanidm/kanidm/blob/master/examples/unixd">example configuration</link> + for possible values. + ''; + }; + }; + + config = lib.mkIf (cfg.enableClient || cfg.enableServer || cfg.enablePam) { + assertions = + [ + { + assertion = !cfg.enableServer || ((cfg.serverSettings.tls_chain or null) == null) || (!lib.isStorePath cfg.serverSettings.tls_chain); + message = '' + <option>services.kanidm.serverSettings.tls_chain</option> points to + a file in the Nix store. You should use a quoted absolute path to + prevent this. + ''; + } + { + assertion = !cfg.enableServer || ((cfg.serverSettings.tls_key or null) == null) || (!lib.isStorePath cfg.serverSettings.tls_key); + message = '' + <option>services.kanidm.serverSettings.tls_key</option> points to + a file in the Nix store. You should use a quoted absolute path to + prevent this. + ''; + } + { + assertion = !cfg.enableClient || options.services.kanidm.clientSettings.isDefined; + message = '' + <option>services.kanidm.clientSettings</option> needs to be configured + if the client is enabled. + ''; + } + { + assertion = !cfg.enablePam || options.services.kanidm.clientSettings.isDefined; + message = '' + <option>services.kanidm.clientSettings</option> needs to be configured + for the PAM daemon to connect to the Kanidm server. + ''; + } + { + assertion = !cfg.enableServer || (cfg.serverSettings.domain == null + -> cfg.serverSettings.role == "WriteReplica" || cfg.serverSettings.role == "WriteReplicaNoUI"); + message = '' + <option>services.kanidm.serverSettings.domain</option> can only be set if this instance + is not a ReadOnlyReplica. Otherwise the db would inherit it from + the instance it follows. + ''; + } + ]; + + environment.systemPackages = lib.mkIf cfg.enableClient [ pkgs.kanidm ]; + + systemd.services.kanidm = lib.mkIf cfg.enableServer { + description = "kanidm identity management daemon"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + serviceConfig = defaultServiceConfig // { + StateDirectory = "kanidm"; + StateDirectoryMode = "0700"; + ExecStart = "${pkgs.kanidm}/bin/kanidmd server -c ${serverConfigFile}"; + User = "kanidm"; + Group = "kanidm"; + + AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ]; + CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" ]; + # This would otherwise override the CAP_NET_BIND_SERVICE capability. + PrivateUsers = false; + # Port needs to be exposed to the host network + PrivateNetwork = false; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; + TemporaryFileSystem = "/:ro"; + }; + environment.RUST_LOG = "info"; + }; + + systemd.services.kanidm-unixd = lib.mkIf cfg.enablePam { + description = "Kanidm PAM daemon"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + restartTriggers = [ unixConfigFile clientConfigFile ]; + serviceConfig = defaultServiceConfig // { + CacheDirectory = "kanidm-unixd"; + CacheDirectoryMode = "0700"; + RuntimeDirectory = "kanidm-unixd"; + ExecStart = "${pkgs.kanidm}/bin/kanidm_unixd"; + User = "kanidm-unixd"; + Group = "kanidm-unixd"; + + BindReadOnlyPaths = [ + "/nix/store" + "-/etc/resolv.conf" + "-/etc/nsswitch.conf" + "-/etc/hosts" + "-/etc/localtime" + "-/etc/kanidm" + "-/etc/static/kanidm" + ]; + BindPaths = [ + # To create the socket + "/run/kanidm-unixd:/var/run/kanidm-unixd" + ]; + # Needs to connect to kanidmd + PrivateNetwork = false; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ]; + TemporaryFileSystem = "/:ro"; + }; + environment.RUST_LOG = "info"; + }; + + systemd.services.kanidm-unixd-tasks = lib.mkIf cfg.enablePam { + description = "Kanidm PAM home management daemon"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" "kanidm-unixd.service" ]; + partOf = [ "kanidm-unixd.service" ]; + restartTriggers = [ unixConfigFile clientConfigFile ]; + serviceConfig = { + ExecStart = "${pkgs.kanidm}/bin/kanidm_unixd_tasks"; + + BindReadOnlyPaths = [ + "/nix/store" + "-/etc/resolv.conf" + "-/etc/nsswitch.conf" + "-/etc/hosts" + "-/etc/localtime" + "-/etc/kanidm" + "-/etc/static/kanidm" + ]; + BindPaths = [ + # To manage home directories + "/home" + # To connect to kanidm-unixd + "/run/kanidm-unixd:/var/run/kanidm-unixd" + ]; + # CAP_DAC_OVERRIDE is needed to ignore ownership of unixd socket + CapabilityBoundingSet = [ "CAP_CHOWN" "CAP_FOWNER" "CAP_DAC_OVERRIDE" "CAP_DAC_READ_SEARCH" ]; + IPAddressDeny = "any"; + # Need access to users + PrivateUsers = false; + # Need access to home directories + ProtectHome = false; + RestrictAddressFamilies = [ "AF_UNIX" ]; + TemporaryFileSystem = "/:ro"; + }; + environment.RUST_LOG = "info"; + }; + + # These paths are hardcoded + environment.etc = lib.mkMerge [ + (lib.mkIf options.services.kanidm.clientSettings.isDefined { + "kanidm/config".source = clientConfigFile; + }) + (lib.mkIf cfg.enablePam { + "kanidm/unixd".source = unixConfigFile; + }) + ]; + + system.nssModules = lib.mkIf cfg.enablePam [ pkgs.kanidm ]; + + system.nssDatabases.group = lib.optional cfg.enablePam "kanidm"; + system.nssDatabases.passwd = lib.optional cfg.enablePam "kanidm"; + + users.groups = lib.mkMerge [ + (lib.mkIf cfg.enableServer { + kanidm = { }; + }) + (lib.mkIf cfg.enablePam { + kanidm-unixd = { }; + }) + ]; + users.users = lib.mkMerge [ + (lib.mkIf cfg.enableServer { + kanidm = { + description = "Kanidm server"; + isSystemUser = true; + group = "kanidm"; + packages = with pkgs; [ kanidm ]; + }; + }) + (lib.mkIf cfg.enablePam { + kanidm-unixd = { + description = "Kanidm PAM daemon"; + isSystemUser = true; + group = "kanidm-unixd"; + }; + }) + ]; + }; + + meta.maintainers = with lib.maintainers; [ erictapen Flakebi ]; + meta.buildDocsInSandbox = false; +} diff --git a/nixos/modules/services/security/sshguard.nix b/nixos/modules/services/security/sshguard.nix index 53bd9efa5ac7b..3be0a8c700b9e 100644 --- a/nixos/modules/services/security/sshguard.nix +++ b/nixos/modules/services/security/sshguard.nix @@ -17,7 +17,7 @@ let else "sshg-fw-ipset"; in pkgs.writeText "sshguard.conf" '' BACKEND="${pkgs.sshguard}/libexec/${backend}" - LOGREADER="LANG=C ${pkgs.systemd}/bin/journalctl ${args}" + LOGREADER="LANG=C ${config.systemd.package}/bin/journalctl ${args}" ''; in { diff --git a/nixos/modules/services/wayland/cage.nix b/nixos/modules/services/wayland/cage.nix index a32b81a916fc2..b818f5c463a91 100644 --- a/nixos/modules/services/wayland/cage.nix +++ b/nixos/modules/services/wayland/cage.nix @@ -88,7 +88,7 @@ in { account required pam_unix.so session required pam_unix.so session required pam_env.so conffile=/etc/pam/environment readenv=0 - session required ${pkgs.systemd}/lib/security/pam_systemd.so + session required ${config.systemd.package}/lib/security/pam_systemd.so ''; hardware.opengl.enable = mkDefault true; diff --git a/nixos/modules/services/web-apps/keycloak.nix b/nixos/modules/services/web-apps/keycloak.nix index 2d817ca19234b..a1855e1c1a791 100644 --- a/nixos/modules/services/web-apps/keycloak.nix +++ b/nixos/modules/services/web-apps/keycloak.nix @@ -540,7 +540,8 @@ in db = if cfg.database.type == "postgresql" then "postgres" else cfg.database.type; db-username = if databaseActuallyCreateLocally then "keycloak" else cfg.database.username; db-password._secret = cfg.database.passwordFile; - db-url-host = "${cfg.database.host}:${toString cfg.database.port}"; + db-url-host = cfg.database.host; + db-url-port = toString cfg.database.port; db-url-database = if databaseActuallyCreateLocally then "keycloak" else cfg.database.name; db-url-properties = prefixUnlessEmpty "?" dbProps; db-url = null; diff --git a/nixos/modules/services/web-apps/restya-board.nix b/nixos/modules/services/web-apps/restya-board.nix index 4b36cc8754c61..0bfa2368787ff 100644 --- a/nixos/modules/services/web-apps/restya-board.nix +++ b/nixos/modules/services/web-apps/restya-board.nix @@ -294,7 +294,7 @@ in ln -sf "${cfg.dataDir}/client/img" "${runDir}/client/img" chmod g+w "${runDir}/tmp/cache" - chown -R "${cfg.user}"."${cfg.group}" "${runDir}" + chown -R "${cfg.user}":"${cfg.group}" "${runDir}" mkdir -m 0750 -p "${cfg.dataDir}" @@ -302,9 +302,9 @@ in mkdir -m 0750 -p "${cfg.dataDir}/client/img" cp -r "${pkgs.restya-board}/media/"* "${cfg.dataDir}/media" cp -r "${pkgs.restya-board}/client/img/"* "${cfg.dataDir}/client/img" - chown "${cfg.user}"."${cfg.group}" "${cfg.dataDir}" - chown -R "${cfg.user}"."${cfg.group}" "${cfg.dataDir}/media" - chown -R "${cfg.user}"."${cfg.group}" "${cfg.dataDir}/client/img" + chown "${cfg.user}":"${cfg.group}" "${cfg.dataDir}" + chown -R "${cfg.user}":"${cfg.group}" "${cfg.dataDir}/media" + chown -R "${cfg.user}":"${cfg.group}" "${cfg.dataDir}/client/img" ${optionalString (cfg.database.host == null) '' if ! [ -e "${cfg.dataDir}/.db-initialized" ]; then diff --git a/nixos/modules/services/web-apps/snipe-it.nix b/nixos/modules/services/web-apps/snipe-it.nix new file mode 100644 index 0000000000000..f14171d02f351 --- /dev/null +++ b/nixos/modules/services/web-apps/snipe-it.nix @@ -0,0 +1,493 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.snipe-it; + snipe-it = pkgs.snipe-it.override { + dataDir = cfg.dataDir; + }; + db = cfg.database; + mail = cfg.mail; + + user = cfg.user; + group = cfg.group; + + tlsEnabled = cfg.nginx.addSSL || cfg.nginx.forceSSL || cfg.nginx.onlySSL || cfg.nginx.enableACME; + + # shell script for local administration + artisan = pkgs.writeScriptBin "snipe-it" '' + #! ${pkgs.runtimeShell} + cd ${snipe-it} + sudo=exec + if [[ "$USER" != ${user} ]]; then + sudo='exec /run/wrappers/bin/sudo -u ${user}' + fi + $sudo ${pkgs.php}/bin/php artisan $* + ''; +in { + options.services.snipe-it = { + + enable = mkEnableOption "A free open source IT asset/license management system"; + + user = mkOption { + default = "snipeit"; + description = "User snipe-it runs as."; + type = types.str; + }; + + group = mkOption { + default = "snipeit"; + description = "Group snipe-it runs as."; + type = types.str; + }; + + appKeyFile = mkOption { + description = '' + A file containing the Laravel APP_KEY - a 32 character long, + base64 encoded key used for encryption where needed. Can be + generated with <code>head -c 32 /dev/urandom | base64</code>. + ''; + example = "/run/keys/snipe-it/appkey"; + type = types.path; + }; + + hostName = lib.mkOption { + type = lib.types.str; + default = if config.networking.domain != null then + config.networking.fqdn + else + config.networking.hostName; + defaultText = lib.literalExpression "config.networking.fqdn"; + example = "snipe-it.example.com"; + description = '' + The hostname to serve Snipe-IT on. + ''; + }; + + appURL = mkOption { + description = '' + The root URL that you want to host Snipe-IT on. All URLs in Snipe-IT will be generated using this value. + If you change this in the future you may need to run a command to update stored URLs in the database. + Command example: <code>snipe-it snipe-it:update-url https://old.example.com https://new.example.com</code> + ''; + default = "http${lib.optionalString tlsEnabled "s"}://${cfg.hostName}"; + defaultText = '' + http''${lib.optionalString tlsEnabled "s"}://''${cfg.hostName} + ''; + example = "https://example.com"; + type = types.str; + }; + + dataDir = mkOption { + description = "snipe-it data directory"; + default = "/var/lib/snipe-it"; + type = types.path; + }; + + database = { + host = mkOption { + type = types.str; + default = "localhost"; + description = "Database host address."; + }; + port = mkOption { + type = types.port; + default = 3306; + description = "Database host port."; + }; + name = mkOption { + type = types.str; + default = "snipeit"; + description = "Database name."; + }; + user = mkOption { + type = types.str; + default = user; + defaultText = literalExpression "user"; + description = "Database username."; + }; + passwordFile = mkOption { + type = with types; nullOr path; + default = null; + example = "/run/keys/snipe-it/dbpassword"; + description = '' + A file containing the password corresponding to + <option>database.user</option>. + ''; + }; + createLocally = mkOption { + type = types.bool; + default = false; + description = "Create the database and database user locally."; + }; + }; + + mail = { + driver = mkOption { + type = types.enum [ "smtp" "sendmail" ]; + default = "smtp"; + description = "Mail driver to use."; + }; + host = mkOption { + type = types.str; + default = "localhost"; + description = "Mail host address."; + }; + port = mkOption { + type = types.port; + default = 1025; + description = "Mail host port."; + }; + encryption = mkOption { + type = with types; nullOr (enum [ "tls" "ssl" ]); + default = null; + description = "SMTP encryption mechanism to use."; + }; + user = mkOption { + type = with types; nullOr str; + default = null; + example = "snipeit"; + description = "Mail username."; + }; + passwordFile = mkOption { + type = with types; nullOr path; + default = null; + example = "/run/keys/snipe-it/mailpassword"; + description = '' + A file containing the password corresponding to + <option>mail.user</option>. + ''; + }; + backupNotificationAddress = mkOption { + type = types.str; + default = "backup@example.com"; + description = "Email Address to send Backup Notifications to."; + }; + from = { + name = mkOption { + type = types.str; + default = "Snipe-IT Asset Management"; + description = "Mail \"from\" name."; + }; + address = mkOption { + type = types.str; + default = "mail@example.com"; + description = "Mail \"from\" address."; + }; + }; + replyTo = { + name = mkOption { + type = types.str; + default = "Snipe-IT Asset Management"; + description = "Mail \"reply-to\" name."; + }; + address = mkOption { + type = types.str; + default = "mail@example.com"; + description = "Mail \"reply-to\" address."; + }; + }; + }; + + maxUploadSize = mkOption { + type = types.str; + default = "18M"; + example = "1G"; + description = "The maximum size for uploads (e.g. images)."; + }; + + poolConfig = mkOption { + type = with types; attrsOf (oneOf [ str int bool ]); + default = { + "pm" = "dynamic"; + "pm.max_children" = 32; + "pm.start_servers" = 2; + "pm.min_spare_servers" = 2; + "pm.max_spare_servers" = 4; + "pm.max_requests" = 500; + }; + description = '' + Options for the snipe-it PHP pool. See the documentation on <literal>php-fpm.conf</literal> + for details on configuration directives. + ''; + }; + + nginx = mkOption { + type = types.submodule ( + recursiveUpdate + (import ../web-servers/nginx/vhost-options.nix { inherit config lib; }) {} + ); + default = {}; + example = literalExpression '' + { + serverAliases = [ + "snipe-it.''${config.networking.domain}" + ]; + # To enable encryption and let let's encrypt take care of certificate + forceSSL = true; + enableACME = true; + } + ''; + description = '' + With this option, you can customize the nginx virtualHost settings. + ''; + }; + + config = mkOption { + type = with types; + attrsOf + (nullOr + (either + (oneOf [ + bool + int + port + path + str + ]) + (submodule { + options = { + _secret = mkOption { + type = nullOr (oneOf [ str path ]); + description = '' + The path to a file containing the value the + option should be set to in the final + configuration file. + ''; + }; + }; + }))); + default = {}; + example = literalExpression '' + { + ALLOWED_IFRAME_HOSTS = "https://example.com"; + WKHTMLTOPDF = "''${pkgs.wkhtmltopdf}/bin/wkhtmltopdf"; + AUTH_METHOD = "oidc"; + OIDC_NAME = "MyLogin"; + OIDC_DISPLAY_NAME_CLAIMS = "name"; + OIDC_CLIENT_ID = "snipe-it"; + OIDC_CLIENT_SECRET = {_secret = "/run/keys/oidc_secret"}; + OIDC_ISSUER = "https://keycloak.example.com/auth/realms/My%20Realm"; + OIDC_ISSUER_DISCOVER = true; + } + ''; + description = '' + Snipe-IT configuration options to set in the + <filename>.env</filename> file. + Refer to <link xlink:href="https://snipe-it.readme.io/docs/configuration"/> + for details on supported values. + + Settings containing secret data should be set to an attribute + set containing the attribute <literal>_secret</literal> - a + string pointing to a file containing the value the option + should be set to. See the example to get a better picture of + this: in the resulting <filename>.env</filename> file, the + <literal>OIDC_CLIENT_SECRET</literal> key will be set to the + contents of the <filename>/run/keys/oidc_secret</filename> + file. + ''; + }; + }; + + config = mkIf cfg.enable { + + assertions = [ + { assertion = db.createLocally -> db.user == user; + message = "services.snipe-it.database.user must be set to ${user} if services.snipe-it.database.createLocally is set true."; + } + { assertion = db.createLocally -> db.passwordFile == null; + message = "services.snipe-it.database.passwordFile cannot be specified if services.snipe-it.database.createLocally is set to true."; + } + ]; + + environment.systemPackages = [ artisan ]; + + services.snipe-it.config = { + APP_ENV = "production"; + APP_KEY._secret = cfg.appKeyFile; + APP_URL = cfg.appURL; + DB_HOST = db.host; + DB_PORT = db.port; + DB_DATABASE = db.name; + DB_USERNAME = db.user; + DB_PASSWORD._secret = db.passwordFile; + MAIL_DRIVER = mail.driver; + MAIL_FROM_NAME = mail.from.name; + MAIL_FROM_ADDR = mail.from.address; + MAIL_REPLYTO_NAME = mail.from.name; + MAIL_REPLYTO_ADDR = mail.from.address; + MAIL_BACKUP_NOTIFICATION_ADDRESS = mail.backupNotificationAddress; + MAIL_HOST = mail.host; + MAIL_PORT = mail.port; + MAIL_USERNAME = mail.user; + MAIL_ENCRYPTION = mail.encryption; + MAIL_PASSWORD._secret = mail.passwordFile; + APP_SERVICES_CACHE = "/run/snipe-it/cache/services.php"; + APP_PACKAGES_CACHE = "/run/snipe-it/cache/packages.php"; + APP_CONFIG_CACHE = "/run/snipe-it/cache/config.php"; + APP_ROUTES_CACHE = "/run/snipe-it/cache/routes-v7.php"; + APP_EVENTS_CACHE = "/run/snipe-it/cache/events.php"; + SESSION_SECURE_COOKIE = tlsEnabled; + }; + + services.mysql = mkIf db.createLocally { + enable = true; + package = mkDefault pkgs.mariadb; + ensureDatabases = [ db.name ]; + ensureUsers = [ + { name = db.user; + ensurePermissions = { "${db.name}.*" = "ALL PRIVILEGES"; }; + } + ]; + }; + + services.phpfpm.pools.snipe-it = { + inherit user group; + phpPackage = pkgs.php74; + phpOptions = '' + post_max_size = ${cfg.maxUploadSize} + upload_max_filesize = ${cfg.maxUploadSize} + ''; + settings = { + "listen.mode" = "0660"; + "listen.owner" = user; + "listen.group" = group; + } // cfg.poolConfig; + }; + + services.nginx = { + enable = mkDefault true; + virtualHosts."${cfg.hostName}" = mkMerge [ cfg.nginx { + root = mkForce "${snipe-it}/public"; + extraConfig = optionalString (cfg.nginx.addSSL || cfg.nginx.forceSSL || cfg.nginx.onlySSL || cfg.nginx.enableACME) "fastcgi_param HTTPS on;"; + locations = { + "/" = { + index = "index.php"; + extraConfig = ''try_files $uri $uri/ /index.php?$query_string;''; + }; + "~ \.php$" = { + extraConfig = '' + try_files $uri $uri/ /index.php?$query_string; + include ${config.services.nginx.package}/conf/fastcgi_params; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param REDIRECT_STATUS 200; + fastcgi_pass unix:${config.services.phpfpm.pools."snipe-it".socket}; + ${optionalString (cfg.nginx.addSSL || cfg.nginx.forceSSL || cfg.nginx.onlySSL || cfg.nginx.enableACME) "fastcgi_param HTTPS on;"} + ''; + }; + "~ \.(js|css|gif|png|ico|jpg|jpeg)$" = { + extraConfig = "expires 365d;"; + }; + }; + }]; + }; + + systemd.services.snipe-it-setup = { + description = "Preperation tasks for snipe-it"; + before = [ "phpfpm-snipe-it.service" ]; + after = optional db.createLocally "mysql.service"; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + User = user; + WorkingDirectory = snipe-it; + RuntimeDirectory = "snipe-it/cache"; + RuntimeDirectoryMode = 0700; + }; + path = [ pkgs.replace-secret ]; + script = + let + isSecret = v: isAttrs v && v ? _secret && (isString v._secret || builtins.isPath v._secret); + snipeITEnvVars = lib.generators.toKeyValue { + mkKeyValue = lib.flip lib.generators.mkKeyValueDefault "=" { + mkValueString = v: with builtins; + if isInt v then toString v + else if isString v then "\"${v}\"" + else if true == v then "true" + else if false == v then "false" + else if isSecret v then + if (isString v._secret) then + hashString "sha256" v._secret + else + hashString "sha256" (builtins.readFile v._secret) + else throw "unsupported type ${typeOf v}: ${(lib.generators.toPretty {}) v}"; + }; + }; + secretPaths = lib.mapAttrsToList (_: v: v._secret) (lib.filterAttrs (_: isSecret) cfg.config); + mkSecretReplacement = file: '' + replace-secret ${escapeShellArgs [ + ( + if (isString file) then + builtins.hashString "sha256" file + else + builtins.hashString "sha256" (builtins.readFile file) + ) + file + "${cfg.dataDir}/.env" + ]} + ''; + secretReplacements = lib.concatMapStrings mkSecretReplacement secretPaths; + filteredConfig = lib.converge (lib.filterAttrsRecursive (_: v: ! elem v [ {} null ])) cfg.config; + snipeITEnv = pkgs.writeText "snipeIT.env" (snipeITEnvVars filteredConfig); + in '' + # error handling + set -euo pipefail + + # set permissions + umask 077 + + # create .env file + install -T -m 0600 -o ${user} ${snipeITEnv} "${cfg.dataDir}/.env" + + # replace secrets + ${secretReplacements} + + # prepend `base64:` if it does not exist in APP_KEY + if ! grep 'APP_KEY=base64:' "${cfg.dataDir}/.env" >/dev/null; then + sed -i 's/APP_KEY=/APP_KEY=base64:/' "${cfg.dataDir}/.env" + fi + + # purge cache + rm "${cfg.dataDir}"/bootstrap/cache/*.php || true + + # migrate db + ${pkgs.php}/bin/php artisan migrate --force + ''; + }; + + systemd.tmpfiles.rules = [ + "d ${cfg.dataDir} 0710 ${user} ${group} - -" + "d ${cfg.dataDir}/bootstrap 0750 ${user} ${group} - -" + "d ${cfg.dataDir}/bootstrap/cache 0750 ${user} ${group} - -" + "d ${cfg.dataDir}/public 0750 ${user} ${group} - -" + "d ${cfg.dataDir}/public/uploads 0750 ${user} ${group} - -" + "d ${cfg.dataDir}/storage 0700 ${user} ${group} - -" + "d ${cfg.dataDir}/storage/app 0700 ${user} ${group} - -" + "d ${cfg.dataDir}/storage/fonts 0700 ${user} ${group} - -" + "d ${cfg.dataDir}/storage/framework 0700 ${user} ${group} - -" + "d ${cfg.dataDir}/storage/framework/cache 0700 ${user} ${group} - -" + "d ${cfg.dataDir}/storage/framework/sessions 0700 ${user} ${group} - -" + "d ${cfg.dataDir}/storage/framework/views 0700 ${user} ${group} - -" + "d ${cfg.dataDir}/storage/logs 0700 ${user} ${group} - -" + "d ${cfg.dataDir}/storage/uploads 0700 ${user} ${group} - -" + ]; + + users = { + users = mkIf (user == "snipeit") { + snipeit = { + inherit group; + isSystemUser = true; + }; + "${config.services.nginx.user}".extraGroups = [ group ]; + }; + groups = mkIf (group == "snipeit") { + snipeit = {}; + }; + }; + + }; + + meta.maintainers = with maintainers; [ yayayayaka ]; +} diff --git a/nixos/modules/services/x11/desktop-managers/default.nix b/nixos/modules/services/x11/desktop-managers/default.nix index 8247a7e381c90..2c2f2cae4b749 100644 --- a/nixos/modules/services/x11/desktop-managers/default.nix +++ b/nixos/modules/services/x11/desktop-managers/default.nix @@ -18,7 +18,7 @@ in # determines the default: later modules (if enabled) are preferred. # E.g., if Plasma 5 is enabled, it supersedes xterm. imports = [ - ./none.nix ./xterm.nix ./xfce.nix ./plasma5.nix ./lumina.nix + ./none.nix ./xterm.nix ./phosh.nix ./xfce.nix ./plasma5.nix ./lumina.nix ./lxqt.nix ./enlightenment.nix ./gnome.nix ./retroarch.nix ./kodi.nix ./mate.nix ./pantheon.nix ./surf-display.nix ./cde.nix ./cinnamon.nix @@ -72,7 +72,7 @@ in apply = map (d: d // { manage = "desktop"; start = d.start - + optionalString (needBGCond d) '' + + optionalString (needBGCond d) ''\n\n if [ -e $HOME/.background-image ]; then ${pkgs.feh}/bin/feh --bg-${cfg.wallpaper.mode} ${optionalString cfg.wallpaper.combineScreens "--no-xinerama"} $HOME/.background-image fi diff --git a/nixos/modules/programs/phosh.nix b/nixos/modules/services/x11/desktop-managers/phosh.nix index ad875616ac9e6..4bf78fa16e7d9 100644 --- a/nixos/modules/programs/phosh.nix +++ b/nixos/modules/services/x11/desktop-managers/phosh.nix @@ -3,7 +3,7 @@ with lib; let - cfg = config.programs.phosh; + cfg = config.services.xserver.desktopManager.phosh; # Based on https://source.puri.sm/Librem5/librem5-base/-/blob/4596c1056dd75ac7f043aede07887990fd46f572/default/sm.puri.OSK0.desktop oskItem = pkgs.makeDesktopItem { @@ -118,12 +118,39 @@ let [cursor] theme = ${phoc.cursorTheme} ''; -in { +in + +{ options = { - programs.phosh = { - enable = mkEnableOption '' - Whether to enable, Phosh, related packages and default configurations. - ''; + services.xserver.desktopManager.phosh = { + enable = mkOption { + type = types.bool; + default = false; + description = "Enable the Phone Shell."; + }; + + package = mkOption { + type = types.package; + default = pkgs.phosh; + defaultText = literalExpression "pkgs.phosh"; + example = literalExpression "pkgs.phosh"; + description = '' + Package that should be used for Phosh. + ''; + }; + + user = mkOption { + description = "The user to run the Phosh service."; + type = types.str; + example = "alice"; + }; + + group = mkOption { + description = "The group to run the Phosh service."; + type = types.str; + example = "users"; + }; + phocConfig = mkOption { description = '' Configurations for the Phoc compositor. @@ -135,14 +162,42 @@ in { }; config = mkIf cfg.enable { + systemd.defaultUnit = "graphical.target"; + # Inspired by https://gitlab.gnome.org/World/Phosh/phosh/-/blob/main/data/phosh.service + systemd.services.phosh = { + wantedBy = [ "graphical.target" ]; + serviceConfig = { + ExecStart = "${cfg.package}/bin/phosh"; + User = cfg.user; + Group = cfg.group; + PAMName = "login"; + WorkingDirectory = "~"; + Restart = "always"; + + TTYPath = "/dev/tty7"; + TTYReset = "yes"; + TTYVHangup = "yes"; + TTYVTDisallocate = "yes"; + + # Fail to start if not controlling the tty. + StandardInput = "tty-fail"; + StandardOutput = "journal"; + StandardError = "journal"; + + # Log this user with utmp, letting it show up with commands 'w' and 'who'. + UtmpIdentifier = "tty7"; + UtmpMode = "user"; + }; + }; + environment.systemPackages = [ pkgs.phoc - pkgs.phosh + cfg.package pkgs.squeekboard oskItem ]; - systemd.packages = [ pkgs.phosh ]; + systemd.packages = [ cfg.package ]; programs.feedbackd.enable = true; @@ -152,7 +207,7 @@ in { services.gnome.core-shell.enable = true; services.gnome.core-os-services.enable = true; - services.xserver.displayManager.sessionPackages = [ pkgs.phosh ]; + services.xserver.displayManager.sessionPackages = [ cfg.package ]; environment.etc."phosh/phoc.ini".source = if builtins.isPath cfg.phocConfig then cfg.phocConfig diff --git a/nixos/modules/services/x11/display-managers/gdm.nix b/nixos/modules/services/x11/display-managers/gdm.nix index 70ae6b8978d0b..4941d13c810ed 100644 --- a/nixos/modules/services/x11/display-managers/gdm.nix +++ b/nixos/modules/services/x11/display-managers/gdm.nix @@ -298,7 +298,7 @@ in session required pam_succeed_if.so audit quiet_success user = gdm session required pam_env.so conffile=/etc/pam/environment readenv=0 - session optional ${pkgs.systemd}/lib/security/pam_systemd.so + session optional ${config.systemd.package}/lib/security/pam_systemd.so session optional pam_keyinit.so force revoke session optional pam_permit.so ''; diff --git a/nixos/modules/services/x11/display-managers/lightdm.nix b/nixos/modules/services/x11/display-managers/lightdm.nix index 27dfed3cc14c1..302c8fe0d9171 100644 --- a/nixos/modules/services/x11/display-managers/lightdm.nix +++ b/nixos/modules/services/x11/display-managers/lightdm.nix @@ -287,7 +287,7 @@ in session required pam_succeed_if.so audit quiet_success user = lightdm session required pam_env.so conffile=/etc/pam/environment readenv=0 - session optional ${pkgs.systemd}/lib/security/pam_systemd.so + session optional ${config.systemd.package}/lib/security/pam_systemd.so session optional pam_keyinit.so force revoke session optional pam_permit.so ''; diff --git a/nixos/modules/services/x11/display-managers/sddm.nix b/nixos/modules/services/x11/display-managers/sddm.nix index 529a086381f06..c44f24002e0b9 100644 --- a/nixos/modules/services/x11/display-managers/sddm.nix +++ b/nixos/modules/services/x11/display-managers/sddm.nix @@ -231,7 +231,7 @@ in session required pam_succeed_if.so audit quiet_success user = sddm session required pam_env.so conffile=/etc/pam/environment readenv=0 - session optional ${pkgs.systemd}/lib/security/pam_systemd.so + session optional ${config.systemd.package}/lib/security/pam_systemd.so session optional pam_keyinit.so force revoke session optional pam_permit.so ''; diff --git a/nixos/modules/system/boot/modprobe.nix b/nixos/modules/system/boot/modprobe.nix index e683d1817297a..21be18ef866eb 100644 --- a/nixos/modules/system/boot/modprobe.nix +++ b/nixos/modules/system/boot/modprobe.nix @@ -52,7 +52,7 @@ with lib; ''; environment.etc."modprobe.d/debian.conf".source = pkgs.kmod-debian-aliases; - environment.etc."modprobe.d/systemd.conf".source = "${pkgs.systemd}/lib/modprobe.d/systemd.conf"; + environment.etc."modprobe.d/systemd.conf".source = "${config.systemd.package}/lib/modprobe.d/systemd.conf"; environment.systemPackages = [ pkgs.kmod ]; diff --git a/nixos/modules/system/boot/networkd.nix b/nixos/modules/system/boot/networkd.nix index d1a6f46bfc408..0336930b3ab7d 100644 --- a/nixos/modules/system/boot/networkd.nix +++ b/nixos/modules/system/boot/networkd.nix @@ -779,6 +779,7 @@ let "RouteDenyList" "RouteAllowList" "DHCPv6Client" + "RouteMetric" ]) (assertValueOneOf "UseDNS" boolValues) (assertValueOneOf "UseDomains" (boolValues ++ ["route"])) diff --git a/nixos/modules/system/boot/plymouth.nix b/nixos/modules/system/boot/plymouth.nix index 78ae8e9d20b77..8b57cae3c40da 100644 --- a/nixos/modules/system/boot/plymouth.nix +++ b/nixos/modules/system/boot/plymouth.nix @@ -4,7 +4,10 @@ with lib; let - inherit (pkgs) plymouth nixos-icons; + inherit (pkgs) nixos-icons; + plymouth = pkgs.plymouth.override { + systemd = config.boot.initrd.systemd.package; + }; cfg = config.boot.plymouth; opt = options.boot.plymouth; @@ -143,7 +146,88 @@ in systemd.services.systemd-ask-password-plymouth.wantedBy = [ "multi-user.target" ]; systemd.paths.systemd-ask-password-plymouth.wantedBy = [ "multi-user.target" ]; - boot.initrd.extraUtilsCommands = '' + boot.initrd.systemd = { + extraBin.plymouth = "${plymouth}/bin/plymouth"; # for the recovery shell + storePaths = [ + "${lib.getBin config.boot.initrd.systemd.package}/bin/systemd-tty-ask-password-agent" + "${plymouth}/bin/plymouthd" + "${plymouth}/sbin/plymouthd" + ]; + packages = [ plymouth ]; # systemd units + contents = { + # Files + "/etc/plymouth/plymouthd.conf".source = configFile; + "/etc/plymouth/plymouthd.defaults".source = "${plymouth}/share/plymouth/plymouthd.defaults"; + "/etc/plymouth/logo.png".source = cfg.logo; + # Directories + "/etc/plymouth/plugins".source = pkgs.runCommand "plymouth-initrd-plugins" {} '' + # Check if the actual requested theme is here + if [[ ! -d ${themesEnv}/share/plymouth/themes/${cfg.theme} ]]; then + echo "The requested theme: ${cfg.theme} is not provided by any of the packages in boot.plymouth.themePackages" + exit 1 + fi + + moduleName="$(sed -n 's,ModuleName *= *,,p' ${themesEnv}/share/plymouth/themes/${cfg.theme}/${cfg.theme}.plymouth)" + + mkdir -p $out/renderers + # module might come from a theme + cp ${themesEnv}/lib/plymouth/{text,details,label,$moduleName}.so $out + cp ${plymouth}/lib/plymouth/renderers/{drm,frame-buffer}.so $out/renderers + ''; + "/etc/plymouth/themes".source = pkgs.runCommand "plymouth-initrd-themes" {} '' + # Check if the actual requested theme is here + if [[ ! -d ${themesEnv}/share/plymouth/themes/${cfg.theme} ]]; then + echo "The requested theme: ${cfg.theme} is not provided by any of the packages in boot.plymouth.themePackages" + exit 1 + fi + + mkdir $out + cp -r ${themesEnv}/share/plymouth/themes/${cfg.theme} $out + # Copy more themes if the theme depends on others + for theme in $(grep -hRo '/etc/plymouth/themes/.*$' ${themesEnv} | xargs -n1 basename); do + if [[ -d "${themesEnv}/theme" ]]; then + cp -r "${themesEnv}/theme" $out + fi + done + ''; + + # Fonts + "/etc/plymouth/fonts".source = pkgs.runCommand "plymouth-initrd-fonts" {} '' + mkdir -p $out + cp ${cfg.font} $out + ''; + "/etc/fonts/fonts.conf".text = '' + <?xml version="1.0"?> + <!DOCTYPE fontconfig SYSTEM "urn:fontconfig:fonts.dtd"> + <fontconfig> + <dir>/etc/plymouth/fonts</dir> + </fontconfig> + ''; + }; + # Properly enable units. These are the units that arch copies + services = { + plymouth-halt.wantedBy = [ "halt.target" ]; + plymouth-kexec.wantedBy = [ "kexec.target" ]; + plymouth-poweroff.wantedBy = [ "poweroff.target" ]; + plymouth-quit-wait.wantedBy = [ "multi-user.target" ]; + plymouth-quit.wantedBy = [ "multi-user.target" ]; + plymouth-read-write.wantedBy = [ "sysinit.target" ]; + plymouth-reboot.wantedBy = [ "reboot.target" ]; + plymouth-start.wantedBy = [ "initrd-switch-root.target" "sysinit.target" ]; + plymouth-switch-root-initramfs.wantedBy = [ "halt.target" "kexec.target" "plymouth-switch-root-initramfs.service" "poweroff.target" "reboot.target" ]; + plymouth-switch-root.wantedBy = [ "initrd-switch-root.target" ]; + }; + }; + + # Insert required udev rules. We take stage 2 systemd because the udev + # rules are only generated when building with logind. + boot.initrd.services.udev.packages = [ (pkgs.runCommand "initrd-plymouth-udev-rules" {} '' + mkdir -p $out/etc/udev/rules.d + cp ${config.systemd.package.out}/lib/udev/rules.d/{70-uaccess,71-seat}.rules $out/etc/udev/rules.d + sed -i '/loginctl/d' $out/etc/udev/rules.d/71-seat.rules + '') ]; + + boot.initrd.extraUtilsCommands = lib.mkIf (!config.boot.initrd.systemd.enable) '' copy_bin_and_libs ${plymouth}/bin/plymouth copy_bin_and_libs ${plymouth}/bin/plymouthd @@ -198,18 +282,18 @@ in EOF ''; - boot.initrd.extraUtilsCommandsTest = '' + boot.initrd.extraUtilsCommandsTest = mkIf (!config.boot.initrd.enable) '' $out/bin/plymouthd --help >/dev/null $out/bin/plymouth --help >/dev/null ''; - boot.initrd.extraUdevRulesCommands = '' + boot.initrd.extraUdevRulesCommands = mkIf (!config.boot.initrd.enable) '' cp ${config.systemd.package}/lib/udev/rules.d/{70-uaccess,71-seat}.rules $out sed -i '/loginctl/d' $out/71-seat.rules ''; # We use `mkAfter` to ensure that LUKS password prompt would be shown earlier than the splash screen. - boot.initrd.preLVMCommands = mkAfter '' + boot.initrd.preLVMCommands = mkIf (!config.boot.initrd.enable) (mkAfter '' mkdir -p /etc/plymouth mkdir -p /run/plymouth ln -s ${configFile} /etc/plymouth/plymouthd.conf @@ -221,16 +305,16 @@ in plymouthd --mode=boot --pid-file=/run/plymouth/pid --attach-to-session plymouth show-splash - ''; + ''); - boot.initrd.postMountCommands = '' + boot.initrd.postMountCommands = mkIf (!config.boot.initrd.enable) '' plymouth update-root-fs --new-root-dir="$targetRoot" ''; # `mkBefore` to ensure that any custom prompts would be visible. - boot.initrd.preFailCommands = mkBefore '' + boot.initrd.preFailCommands = mkIf (!config.boot.initrd.enable) (mkBefore '' plymouth quit --wait - ''; + ''); }; diff --git a/nixos/modules/system/boot/systemd/nspawn.nix b/nixos/modules/system/boot/systemd/nspawn.nix index bf9995d03cc18..da03c60db5281 100644 --- a/nixos/modules/system/boot/systemd/nspawn.nix +++ b/nixos/modules/system/boot/systemd/nspawn.nix @@ -16,7 +16,7 @@ let "LimitNOFILE" "LimitAS" "LimitNPROC" "LimitMEMLOCK" "LimitLOCKS" "LimitSIGPENDING" "LimitMSGQUEUE" "LimitNICE" "LimitRTPRIO" "LimitRTTIME" "OOMScoreAdjust" "CPUAffinity" "Hostname" "ResolvConf" "Timezone" - "LinkJournal" + "LinkJournal" "Ephemeral" "AmbientCapability" ]) (assertValueOneOf "Boot" boolValues) (assertValueOneOf "ProcessTwo" boolValues) @@ -26,11 +26,13 @@ let checkFiles = checkUnitConfig "Files" [ (assertOnlyFields [ "ReadOnly" "Volatile" "Bind" "BindReadOnly" "TemporaryFileSystem" - "Overlay" "OverlayReadOnly" "PrivateUsersChown" + "Overlay" "OverlayReadOnly" "PrivateUsersChown" "BindUser" + "Inaccessible" "PrivateUserOwnership" ]) (assertValueOneOf "ReadOnly" boolValues) (assertValueOneOf "Volatile" (boolValues ++ [ "state" ])) (assertValueOneOf "PrivateUsersChown" boolValues) + (assertValueOneOf "PrivateUserOwnership" [ "off" "chown" "map" "auto" ]) ]; checkNetwork = checkUnitConfig "Network" [ diff --git a/nixos/modules/tasks/auto-upgrade.nix b/nixos/modules/tasks/auto-upgrade.nix index d00dc761d6e38..21a25cbfa9643 100644 --- a/nixos/modules/tasks/auto-upgrade.nix +++ b/nixos/modules/tasks/auto-upgrade.nix @@ -190,7 +190,7 @@ in { nixos-rebuild = "${config.system.build.nixos-rebuild}/bin/nixos-rebuild"; date = "${pkgs.coreutils}/bin/date"; readlink = "${pkgs.coreutils}/bin/readlink"; - shutdown = "${pkgs.systemd}/bin/shutdown"; + shutdown = "${config.systemd.package}/bin/shutdown"; upgradeFlag = optional (cfg.channel == null) "--upgrade"; in if cfg.allowReboot then '' ${nixos-rebuild} boot ${toString (cfg.flags ++ upgradeFlag)} diff --git a/nixos/modules/tasks/filesystems/zfs.nix b/nixos/modules/tasks/filesystems/zfs.nix index 5eca68798d5d8..c8bbfe9769b21 100644 --- a/nixos/modules/tasks/filesystems/zfs.nix +++ b/nixos/modules/tasks/filesystems/zfs.nix @@ -58,6 +58,13 @@ let # latter case it makes one last attempt at importing, allowing the system to # (eventually) boot even with a degraded pool. importLib = {zpoolCmd, awkCmd, cfgZfs}: '' + for o in $(cat /proc/cmdline); do + case $o in + zfs_force|zfs_force=1|zfs_force=y) + ZFS_FORCE="-f" + ;; + esac + done poolReady() { pool="$1" state="$("${zpoolCmd}" import 2>/dev/null | "${awkCmd}" "/pool: $pool/ { found = 1 }; /state:/ { if (found == 1) { print \$2; exit } }; END { if (found == 0) { print \"MISSING\" } }")" @@ -78,6 +85,95 @@ let } ''; + getPoolFilesystems = pool: + filter (x: x.fsType == "zfs" && (fsToPool x) == pool) config.system.build.fileSystems; + + getPoolMounts = prefix: pool: + let + # Remove the "/" suffix because even though most mountpoints + # won't have it, the "/" mountpoint will, and we can't have the + # trailing slash in "/sysroot/" in stage 1. + mountPoint = fs: escapeSystemdPath (prefix + (lib.removeSuffix "/" fs.mountPoint)); + in + map (x: "${mountPoint x}.mount") (getPoolFilesystems pool); + + getKeyLocations = pool: + if isBool cfgZfs.requestEncryptionCredentials + then "${cfgZfs.package}/sbin/zfs list -rHo name,keylocation,keystatus ${pool}" + else "${cfgZfs.package}/sbin/zfs list -Ho name,keylocation,keystatus ${toString (filter (x: datasetToPool x == pool) cfgZfs.requestEncryptionCredentials)}"; + + createImportService = { pool, systemd, force, prefix ? "" }: + nameValuePair "zfs-import-${pool}" { + description = "Import ZFS pool \"${pool}\""; + # we need systemd-udev-settle to ensure devices are available + # In the future, hopefully someone will complete this: + # https://github.com/zfsonlinux/zfs/pull/4943 + requires = [ "systemd-udev-settle.service" ]; + after = [ + "systemd-udev-settle.service" + "systemd-modules-load.service" + "systemd-ask-password-console.service" + ]; + wantedBy = (getPoolMounts prefix pool) ++ [ "local-fs.target" ]; + before = (getPoolMounts prefix pool) ++ [ "local-fs.target" ]; + unitConfig = { + DefaultDependencies = "no"; + }; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; + environment.ZFS_FORCE = optionalString force "-f"; + script = (importLib { + # See comments at importLib definition. + zpoolCmd = "${cfgZfs.package}/sbin/zpool"; + awkCmd = "${pkgs.gawk}/bin/awk"; + inherit cfgZfs; + }) + '' + poolImported "${pool}" && exit + echo -n "importing ZFS pool \"${pool}\"..." + # Loop across the import until it succeeds, because the devices needed may not be discovered yet. + for trial in `seq 1 60`; do + poolReady "${pool}" && poolImport "${pool}" && break + sleep 1 + done + poolImported "${pool}" || poolImport "${pool}" # Try one last time, e.g. to import a degraded pool. + if poolImported "${pool}"; then + ${optionalString (if isBool cfgZfs.requestEncryptionCredentials + then cfgZfs.requestEncryptionCredentials + else cfgZfs.requestEncryptionCredentials != []) '' + ${getKeyLocations pool} | while IFS=$'\t' read ds kl ks; do + { + if [[ "$ks" != unavailable ]]; then + continue + fi + case "$kl" in + none ) + ;; + prompt ) + tries=3 + success=false + while [[ $success != true ]] && [[ $tries -gt 0 ]]; do + ${systemd}/bin/systemd-ask-password "Enter key for $ds:" | ${cfgZfs.package}/sbin/zfs load-key "$ds" \ + && success=true \ + || tries=$((tries - 1)) + done + [[ $success = true ]] + ;; + * ) + ${cfgZfs.package}/sbin/zfs load-key "$ds" + ;; + esac + } < /dev/null # To protect while read ds kl in case anything reads stdin + done + ''} + echo "Successfully imported ${pool}" + else + exit 1 + fi + ''; + }; + zedConf = generators.toKeyValue { mkKeyValue = generators.mkKeyValueDefault { mkValueString = v: @@ -428,14 +524,6 @@ in ''; postDeviceCommands = concatStringsSep "\n" (['' ZFS_FORCE="${optionalString cfgZfs.forceImportRoot "-f"}" - - for o in $(cat /proc/cmdline); do - case $o in - zfs_force|zfs_force=1) - ZFS_FORCE="-f" - ;; - esac - done ''] ++ [(importLib { # See comments at importLib definition. zpoolCmd = "zpool"; @@ -464,6 +552,21 @@ in zfs load-key ${fs} '') cfgZfs.requestEncryptionCredentials} '') rootPools)); + + # Systemd in stage 1 + systemd = { + packages = [cfgZfs.package]; + services = listToAttrs (map (pool: createImportService { + inherit pool; + systemd = config.boot.initrd.systemd.package; + force = cfgZfs.forceImportRoot; + prefix = "/sysroot"; + }) rootPools); + extraBin = { + # zpool and zfs are already in thanks to fsPackages + awk = "${pkgs.gawk}/bin/awk"; + }; + }; }; systemd.shutdownRamfs.contents."/etc/systemd/system-shutdown/zpool".source = pkgs.writeShellScript "zpool-sync-shutdown" '' @@ -521,79 +624,11 @@ in systemd.packages = [ cfgZfs.package ]; systemd.services = let - getPoolFilesystems = pool: - filter (x: x.fsType == "zfs" && (fsToPool x) == pool) config.system.build.fileSystems; - - getPoolMounts = pool: - let - mountPoint = fs: escapeSystemdPath fs.mountPoint; - in - map (x: "${mountPoint x}.mount") (getPoolFilesystems pool); - - createImportService = pool: - nameValuePair "zfs-import-${pool}" { - description = "Import ZFS pool \"${pool}\""; - # we need systemd-udev-settle until https://github.com/zfsonlinux/zfs/pull/4943 is merged - requires = [ "systemd-udev-settle.service" ]; - after = [ - "systemd-udev-settle.service" - "systemd-modules-load.service" - "systemd-ask-password-console.service" - ]; - wantedBy = (getPoolMounts pool) ++ [ "local-fs.target" ]; - before = (getPoolMounts pool) ++ [ "local-fs.target" ]; - unitConfig = { - DefaultDependencies = "no"; - }; - serviceConfig = { - Type = "oneshot"; - RemainAfterExit = true; - }; - environment.ZFS_FORCE = optionalString cfgZfs.forceImportAll "-f"; - script = (importLib { - # See comments at importLib definition. - zpoolCmd = "${cfgZfs.package}/sbin/zpool"; - awkCmd = "${pkgs.gawk}/bin/awk"; - inherit cfgZfs; - }) + '' - poolImported "${pool}" && exit - echo -n "importing ZFS pool \"${pool}\"..." - # Loop across the import until it succeeds, because the devices needed may not be discovered yet. - for trial in `seq 1 60`; do - poolReady "${pool}" && poolImport "${pool}" && break - sleep 1 - done - poolImported "${pool}" || poolImport "${pool}" # Try one last time, e.g. to import a degraded pool. - if poolImported "${pool}"; then - ${optionalString (if isBool cfgZfs.requestEncryptionCredentials - then cfgZfs.requestEncryptionCredentials - else cfgZfs.requestEncryptionCredentials != []) '' - ${cfgZfs.package}/sbin/zfs list -rHo name,keylocation ${pool} | while IFS=$'\t' read ds kl; do - { - ${optionalString (!isBool cfgZfs.requestEncryptionCredentials) '' - if ! echo '${concatStringsSep "\n" cfgZfs.requestEncryptionCredentials}' | grep -qFx "$ds"; then - continue - fi - ''} - case "$kl" in - none ) - ;; - prompt ) - ${config.systemd.package}/bin/systemd-ask-password "Enter key for $ds:" | ${cfgZfs.package}/sbin/zfs load-key "$ds" - ;; - * ) - ${cfgZfs.package}/sbin/zfs load-key "$ds" - ;; - esac - } < /dev/null # To protect while read ds kl in case anything reads stdin - done - ''} - echo "Successfully imported ${pool}" - else - exit 1 - fi - ''; - }; + createImportService' = pool: createImportService { + inherit pool; + systemd = config.systemd.package; + force = cfgZfs.forceImportAll; + }; # This forces a sync of any ZFS pools prior to poweroff, even if they're set # to sync=disabled. @@ -619,7 +654,7 @@ in wantedBy = [ "zfs.target" ]; }; - in listToAttrs (map createImportService dataPools ++ + in listToAttrs (map createImportService' dataPools ++ map createSyncService allPools ++ map createZfsService [ "zfs-mount" "zfs-share" "zfs-zed" ]); diff --git a/nixos/modules/tasks/network-interfaces-systemd.nix b/nixos/modules/tasks/network-interfaces-systemd.nix index 8654539b6629c..110e84494a3dc 100644 --- a/nixos/modules/tasks/network-interfaces-systemd.nix +++ b/nixos/modules/tasks/network-interfaces-systemd.nix @@ -43,12 +43,6 @@ in } { assertion = cfg.defaultGateway6 == null || cfg.defaultGateway6.interface == null; message = "networking.defaultGateway6.interface is not supported by networkd."; - } { - assertion = cfg.useDHCP == false; - message = '' - networking.useDHCP is not supported by networkd. - Please use per interface configuration and set the global option to false. - ''; } ] ++ flip mapAttrsToList cfg.bridges (n: { rstp, ... }: { assertion = !rstp; message = "networking.bridges.${n}.rstp is not supported by networkd."; @@ -80,6 +74,42 @@ in in mkMerge [ { enable = true; } + (mkIf cfg.useDHCP { + networks."99-ethernet-default-dhcp" = lib.mkIf cfg.useDHCP { + # We want to match physical ethernet interfaces as commonly + # found on laptops, desktops and servers, to provide an + # "out-of-the-box" setup that works for common cases. This + # heuristic isn't perfect (it could match interfaces with + # custom names that _happen_ to start with en or eth), but + # should be good enough to make the common case easy and can + # be overridden on a case-by-case basis using + # higher-priority networks or by disabling useDHCP. + + # Type=ether matches veth interfaces as well, and this is + # more likely to result in interfaces being configured to + # use DHCP when they shouldn't. + + # We set RequiredForOnline to false, because it's fairly + # common for such devices to have multiple interfaces and + # only one of them to be connected (e.g. a laptop with + # ethernet and WiFi interfaces). Maybe one day networkd will + # support "any"-style RequiredForOnline... + matchConfig.Name = ["en*" "eth*"]; + DHCP = "yes"; + linkConfig.RequiredForOnline = lib.mkDefault false; + }; + networks."99-wireless-client-dhcp" = lib.mkIf cfg.useDHCP { + # Like above, but this is much more likely to be correct. + matchConfig.WLANInterfaceType = "station"; + DHCP = "yes"; + linkConfig.RequiredForOnline = lib.mkDefault false; + # We also set the route metric to one more than the default + # of 1024, so that Ethernet is preferred if both are + # available. + dhcpV4Config.RouteMetric = 1025; + ipv6AcceptRAConfig.RouteMetric = 1025; + }; + }) (mkMerge (forEach interfaces (i: { netdevs = mkIf i.virtual ({ "40-${i.name}" = { diff --git a/nixos/modules/tasks/network-interfaces.nix b/nixos/modules/tasks/network-interfaces.nix index d09e9b99248d8..d56159f15960d 100644 --- a/nixos/modules/tasks/network-interfaces.nix +++ b/nixos/modules/tasks/network-interfaces.nix @@ -1254,11 +1254,6 @@ in Whether to use DHCP to obtain an IP address and other configuration for all network interfaces that are not manually configured. - - Using this option is highly discouraged and also incompatible with - <option>networking.useNetworkd</option>. Please use - <option>networking.interfaces.<name>.useDHCP</option> instead - and set this to false. ''; }; diff --git a/nixos/modules/testing/test-instrumentation.nix b/nixos/modules/testing/test-instrumentation.nix index 81541477b9e09..4ab2578eb81e6 100644 --- a/nixos/modules/testing/test-instrumentation.nix +++ b/nixos/modules/testing/test-instrumentation.nix @@ -129,6 +129,9 @@ in # Make sure we use the Guest Agent from the QEMU package for testing # to reduce the closure size required for the tests. services.qemuGuest.package = pkgs.qemu_test.ga; + + # Squelch warning about unset system.stateVersion + system.stateVersion = lib.mkDefault lib.trivial.release; }; } diff --git a/nixos/modules/virtualisation/amazon-image.nix b/nixos/modules/virtualisation/amazon-image.nix index 9a56b6950155b..12fe6fa444793 100644 --- a/nixos/modules/virtualisation/amazon-image.nix +++ b/nixos/modules/virtualisation/amazon-image.nix @@ -37,13 +37,11 @@ in { assertion = cfg.efi -> cfg.hvm; message = "EC2 instances using EFI must be HVM instances."; } - { assertion = versionOlder config.boot.kernelPackages.kernel.version "5.15"; - message = "ENA driver fails to build with kernel >= 5.15"; + { assertion = versionOlder config.boot.kernelPackages.kernel.version "5.17"; + message = "ENA driver fails to build with kernel >= 5.17"; } ]; - boot.kernelPackages = pkgs.linuxKernel.packages.linux_5_10; - boot.growPartition = cfg.hvm; fileSystems."/" = mkIf (!cfg.zfs.enable) { diff --git a/nixos/modules/virtualisation/amazon-init.nix b/nixos/modules/virtualisation/amazon-init.nix index 4f2f8df90eb8a..9c2adb90bfd21 100644 --- a/nixos/modules/virtualisation/amazon-init.nix +++ b/nixos/modules/virtualisation/amazon-init.nix @@ -11,7 +11,7 @@ let echo "attempting to fetch configuration from EC2 user data..." export HOME=/root - export PATH=${pkgs.lib.makeBinPath [ config.nix.package pkgs.systemd pkgs.gnugrep pkgs.git pkgs.gnutar pkgs.gzip pkgs.gnused pkgs.xz config.system.build.nixos-rebuild]}:$PATH + export PATH=${pkgs.lib.makeBinPath [ config.nix.package config.systemd.package pkgs.gnugrep pkgs.git pkgs.gnutar pkgs.gzip pkgs.gnused pkgs.xz config.system.build.nixos-rebuild]}:$PATH export NIX_PATH=nixpkgs=/nix/var/nix/profiles/per-user/root/channels/nixos:nixos-config=/etc/nixos/configuration.nix:/nix/var/nix/profiles/per-user/root/channels userData=/etc/ec2-metadata/user-data diff --git a/nixos/modules/virtualisation/anbox.nix b/nixos/modules/virtualisation/anbox.nix index a4da62eb5f790..c70da573533fa 100644 --- a/nixos/modules/virtualisation/anbox.nix +++ b/nixos/modules/virtualisation/anbox.nix @@ -73,9 +73,6 @@ in environment.systemPackages = with pkgs; [ anbox ]; - boot.kernelModules = [ "ashmem_linux" "binder_linux" ]; - boot.extraModulePackages = [ kernelPackages.anbox ]; - services.udev.extraRules = '' KERNEL=="ashmem", NAME="%k", MODE="0666" KERNEL=="binder*", NAME="%k", MODE="0666" diff --git a/nixos/modules/virtualisation/digital-ocean-init.nix b/nixos/modules/virtualisation/digital-ocean-init.nix index 4339d91de168e..df30104b7d7fd 100644 --- a/nixos/modules/virtualisation/digital-ocean-init.nix +++ b/nixos/modules/virtualisation/digital-ocean-init.nix @@ -46,7 +46,7 @@ in { RemainAfterExit = true; }; restartIfChanged = false; - path = [ pkgs.jq pkgs.gnused pkgs.gnugrep pkgs.systemd config.nix.package config.system.build.nixos-rebuild ]; + path = [ pkgs.jq pkgs.gnused pkgs.gnugrep config.systemd.package config.nix.package config.system.build.nixos-rebuild ]; environment = { HOME = "/root"; NIX_PATH = concatStringsSep ":" [ diff --git a/nixos/modules/virtualisation/proxmox-lxc.nix b/nixos/modules/virtualisation/proxmox-lxc.nix index 3913b474afbee..9b9f99e5b8172 100644 --- a/nixos/modules/virtualisation/proxmox-lxc.nix +++ b/nixos/modules/virtualisation/proxmox-lxc.nix @@ -20,6 +20,15 @@ with lib; configuration from proxmox. ''; }; + manageHostName = mkOption { + type = types.bool; + default = false; + description = '' + Whether to manage hostname through nix options + When false, the hostname is picked up from /etc/hostname + populated by proxmox. + ''; + }; }; config = @@ -50,6 +59,8 @@ with lib; useDHCP = false; useHostResolvConf = false; useNetworkd = true; + # pick up hostname from /etc/hostname generated by proxmox + hostName = mkIf (!cfg.manageHostName) (mkForce ""); }; services.openssh = { diff --git a/nixos/modules/virtualisation/vmware-host.nix b/nixos/modules/virtualisation/vmware-host.nix new file mode 100644 index 0000000000000..faa0d455c9d6b --- /dev/null +++ b/nixos/modules/virtualisation/vmware-host.nix @@ -0,0 +1,166 @@ +{ config, pkgs, lib, ... }: + +let + cfg = config.virtualisation.vmware.host; + wrapperDir = "/run/vmware/bin"; # Perfectly fits as /usr/local/bin + parentWrapperDir = dirOf wrapperDir; + vmwareWrappers = # Needed as hardcoded paths workaround + let mkVmwareSymlink = + program: + '' + ln -s "${config.security.wrapperDir}/${program}" $wrapperDir/${program} + ''; + in + [ + (mkVmwareSymlink "pkexec") + (mkVmwareSymlink "mount") + (mkVmwareSymlink "umount") + ]; +in +{ + options = with lib; { + virtualisation.vmware.host = { + enable = mkEnableOption "VMware" // { + description = '' + This enables VMware host virtualisation for running VMs. + + <important><para> + <literal>vmware-vmx</literal> will cause kcompactd0 due to + <literal>Transparent Hugepages</literal> feature in kernel. + Apply <literal>[ "transparent_hugepage=never" ]</literal> in + option <option>boot.kernelParams</option> to disable them. + </para></important> + + <note><para> + If that didn't work disable <literal>TRANSPARENT_HUGEPAGE</literal>, + <literal>COMPACTION</literal> configs and recompile kernel. + </para></note> + ''; + }; + package = mkOption { + type = types.package; + default = pkgs.vmware-workstation; + defaultText = literalExpression "pkgs.vmware-workstation"; + description = "VMware host virtualisation package to use"; + }; + extraPackages = mkOption { + type = with types; listOf package; + default = with pkgs; [ ]; + description = "Extra packages to be used with VMware host."; + example = "with pkgs; [ ntfs3g ]"; + }; + extraConfig = mkOption { + type = types.lines; + default = ""; + description = "Add extra config to /etc/vmware/config"; + example = '' + # Allow unsupported device's OpenGL and Vulkan acceleration for guest vGPU + mks.gl.allowUnsupportedDrivers = "TRUE" + mks.vk.allowUnsupportedDevices = "TRUE" + ''; + }; + }; + }; + + config = lib.mkIf cfg.enable { + boot.extraModulePackages = [ config.boot.kernelPackages.vmware ]; + boot.extraModprobeConfig = "alias char-major-10-229 fuse"; + boot.kernelModules = [ "vmw_pvscsi" "vmw_vmci" "vmmon" "vmnet" "fuse" ]; + + environment.systemPackages = [ cfg.package ] ++ cfg.extraPackages; + services.printing.drivers = [ cfg.package ]; + + environment.etc."vmware/config".text = '' + ${builtins.readFile "${cfg.package}/etc/vmware/config"} + ${cfg.extraConfig} + ''; + + environment.etc."vmware/bootstrap".source = "${cfg.package}/etc/vmware/bootstrap"; + environment.etc."vmware/icu".source = "${cfg.package}/etc/vmware/icu"; + environment.etc."vmware-installer".source = "${cfg.package}/etc/vmware-installer"; + + # SUID wrappers + + security.wrappers = { + vmware-vmx = { + setuid = true; + owner = "root"; + group = "root"; + source = "${cfg.package}/lib/vmware/bin/.vmware-vmx-wrapped"; + }; + }; + + ###### wrappers activation script + + system.activationScripts.vmwareWrappers = + lib.stringAfter [ "specialfs" "users" ] + '' + mkdir -p "${parentWrapperDir}" + chmod 755 "${parentWrapperDir}" + # We want to place the tmpdirs for the wrappers to the parent dir. + wrapperDir=$(mktemp --directory --tmpdir="${parentWrapperDir}" wrappers.XXXXXXXXXX) + chmod a+rx "$wrapperDir" + ${lib.concatStringsSep "\n" (vmwareWrappers)} + if [ -L ${wrapperDir} ]; then + # Atomically replace the symlink + # See https://axialcorps.com/2013/07/03/atomically-replacing-files-and-directories/ + old=$(readlink -f ${wrapperDir}) + if [ -e "${wrapperDir}-tmp" ]; then + rm --force --recursive "${wrapperDir}-tmp" + fi + ln --symbolic --force --no-dereference "$wrapperDir" "${wrapperDir}-tmp" + mv --no-target-directory "${wrapperDir}-tmp" "${wrapperDir}" + rm --force --recursive "$old" + else + # For initial setup + ln --symbolic "$wrapperDir" "${wrapperDir}" + fi + ''; + + # Services + + systemd.services."vmware-authdlauncher" = { + description = "VMware Authentification Daemon"; + serviceConfig = { + Type = "forking"; + ExecStart = [ "${cfg.package}/bin/vmware-authdlauncher" ]; + }; + wantedBy = [ "multi-user.target" ]; + }; + + systemd.services."vmware-networks-configuration" = { + description = "VMware Networks Configuration Generation"; + unitConfig.ConditionPathExists = "!/etc/vmware/networking"; + serviceConfig = { + UMask = "0077"; + ExecStart = [ + "${cfg.package}/bin/vmware-networks --postinstall vmware-player,0,1" + ]; + Type = "oneshot"; + RemainAfterExit = "yes"; + }; + wantedBy = [ "multi-user.target" ]; + }; + + systemd.services."vmware-networks" = { + description = "VMware Networks"; + after = [ "vmware-networks-configuration.service" ]; + requires = [ "vmware-networks-configuration.service" ]; + serviceConfig = { + Type = "forking"; + ExecCondition = [ "${pkgs.kmod}/bin/modprobe vmnet" ]; + ExecStart = [ "${cfg.package}/bin/vmware-networks --start" ]; + ExecStop = [ "${cfg.package}/bin/vmware-networks --stop" ]; + }; + wantedBy = [ "multi-user.target" ]; + }; + + systemd.services."vmware-usbarbitrator" = { + description = "VMware USB Arbitrator"; + serviceConfig = { + ExecStart = [ "${cfg.package}/bin/vmware-usbarbitrator -f" ]; + }; + wantedBy = [ "multi-user.target" ]; + }; + }; +} diff --git a/nixos/release.nix b/nixos/release.nix index 6b7564a9b9721..0df443dd204cb 100644 --- a/nixos/release.nix +++ b/nixos/release.nix @@ -150,13 +150,13 @@ in rec { }); iso_plasma5 = forMatchingSystems [ "x86_64-linux" ] (system: makeIso { - module = ./modules/installer/cd-dvd/installation-cd-graphical-plasma5.nix; + module = ./modules/installer/cd-dvd/installation-cd-graphical-calamares-plasma5.nix; type = "plasma5"; inherit system; }); iso_gnome = forMatchingSystems [ "x86_64-linux" ] (system: makeIso { - module = ./modules/installer/cd-dvd/installation-cd-graphical-gnome.nix; + module = ./modules/installer/cd-dvd/installation-cd-graphical-calamares-gnome.nix; type = "gnome"; inherit system; }); diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 923464a0c9a3e..c00f7829ac67e 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -253,6 +253,7 @@ in k3s-single-node = handleTest ./k3s-single-node.nix {}; k3s-single-node-docker = handleTest ./k3s-single-node-docker.nix {}; kafka = handleTest ./kafka.nix {}; + kanidm = handleTest ./kanidm.nix {}; kbd-setfont-decompress = handleTest ./kbd-setfont-decompress.nix {}; kbd-update-search-paths-patch = handleTest ./kbd-update-search-paths-patch.nix {}; kea = handleTest ./kea.nix {}; @@ -284,7 +285,6 @@ in loki = handleTest ./loki.nix {}; lvm2 = handleTest ./lvm2 {}; lxd = handleTest ./lxd.nix {}; - lxd-image = handleTest ./lxd-image.nix {}; lxd-nftables = handleTest ./lxd-nftables.nix {}; lxd-image-server = handleTest ./lxd-image-server.nix {}; #logstash = handleTest ./logstash.nix {}; @@ -456,6 +456,7 @@ in proxy = handleTest ./proxy.nix {}; prowlarr = handleTest ./prowlarr.nix {}; pt2-clone = handleTest ./pt2-clone.nix {}; + public-inbox = handleTest ./public-inbox.nix {}; pulseaudio = discoverTests (import ./pulseaudio.nix); qboot = handleTestOn ["x86_64-linux" "i686-linux"] ./qboot.nix {}; quorum = handleTest ./quorum.nix {}; diff --git a/nixos/tests/common/lxd/config.yaml b/nixos/tests/common/lxd/config.yaml new file mode 100644 index 0000000000000..3bb667ed43f7c --- /dev/null +++ b/nixos/tests/common/lxd/config.yaml @@ -0,0 +1,24 @@ +storage_pools: + - name: default + driver: dir + config: + source: /var/lxd-pool + +networks: + - name: lxdbr0 + type: bridge + config: + ipv4.address: auto + ipv6.address: none + +profiles: + - name: default + devices: + eth0: + name: eth0 + network: lxdbr0 + type: nic + root: + path: / + pool: default + type: disk diff --git a/nixos/tests/dendrite.nix b/nixos/tests/dendrite.nix index a444c9b200189..d4a5bb1322638 100644 --- a/nixos/tests/dendrite.nix +++ b/nixos/tests/dendrite.nix @@ -17,6 +17,7 @@ import ./make-test-python.nix ( homeserver = { pkgs, ... }: { services.dendrite = { enable = true; + openRegistration = true; settings = { global.server_name = "test-dendrite-server.com"; global.private_key = private_key; diff --git a/nixos/tests/installed-tests/default.nix b/nixos/tests/installed-tests/default.nix index fd16b481168f4..c6fb37cfe5842 100644 --- a/nixos/tests/installed-tests/default.nix +++ b/nixos/tests/installed-tests/default.nix @@ -106,6 +106,5 @@ in malcontent = callInstalledTest ./malcontent.nix {}; ostree = callInstalledTest ./ostree.nix {}; pipewire = callInstalledTest ./pipewire.nix {}; - power-profiles-daemon = callInstalledTest ./power-profiles-daemon.nix {}; xdg-desktop-portal = callInstalledTest ./xdg-desktop-portal.nix {}; } diff --git a/nixos/tests/installed-tests/power-profiles-daemon.nix b/nixos/tests/installed-tests/power-profiles-daemon.nix deleted file mode 100644 index 43629a0155d24..0000000000000 --- a/nixos/tests/installed-tests/power-profiles-daemon.nix +++ /dev/null @@ -1,9 +0,0 @@ -{ pkgs, lib, makeInstalledTest, ... }: - -makeInstalledTest { - tested = pkgs.power-profiles-daemon; - - testConfig = { - services.power-profiles-daemon.enable = true; - }; -} diff --git a/nixos/tests/installer-systemd-stage-1.nix b/nixos/tests/installer-systemd-stage-1.nix index a8b418626e660..d02387ee80e09 100644 --- a/nixos/tests/installer-systemd-stage-1.nix +++ b/nixos/tests/installer-systemd-stage-1.nix @@ -27,7 +27,7 @@ simpleUefiGrubSpecialisation simpleUefiSystemdBoot # swraid - # zfsroot + zfsroot ; } diff --git a/nixos/tests/installer.nix b/nixos/tests/installer.nix index d8187d8e019d6..8bef4fad3dd2d 100644 --- a/nixos/tests/installer.nix +++ b/nixos/tests/installer.nix @@ -701,6 +701,85 @@ in { ''; }; + bcachefsSimple = makeInstallerTest "bcachefs-simple" { + extraInstallerConfig = { + boot.supportedFilesystems = [ "bcachefs" ]; + }; + + createPartitions = '' + machine.succeed( + "flock /dev/vda parted --script /dev/vda -- mklabel msdos" + + " mkpart primary ext2 1M 100MB" # /boot + + " mkpart primary linux-swap 100M 1024M" # swap + + " mkpart primary 1024M -1s", # / + "udevadm settle", + "mkswap /dev/vda2 -L swap", + "swapon -L swap", + "mkfs.bcachefs -L root /dev/vda3", + "mount -t bcachefs /dev/vda3 /mnt", + "mkfs.ext3 -L boot /dev/vda1", + "mkdir -p /mnt/boot", + "mount /dev/vda1 /mnt/boot", + ) + ''; + }; + + bcachefsEncrypted = makeInstallerTest "bcachefs-encrypted" { + extraInstallerConfig = { + boot.supportedFilesystems = [ "bcachefs" ]; + environment.systemPackages = with pkgs; [ keyutils ]; + }; + + # We don't want to use the normal way of unlocking bcachefs defined in tasks/filesystems/bcachefs.nix. + # So, override initrd.postDeviceCommands completely and simply unlock with the predefined password. + extraConfig = '' + boot.initrd.postDeviceCommands = lib.mkForce "echo password | bcachefs unlock /dev/vda3"; + ''; + + createPartitions = '' + machine.succeed( + "flock /dev/vda parted --script /dev/vda -- mklabel msdos" + + " mkpart primary ext2 1M 100MB" # /boot + + " mkpart primary linux-swap 100M 1024M" # swap + + " mkpart primary 1024M -1s", # / + "udevadm settle", + "mkswap /dev/vda2 -L swap", + "swapon -L swap", + "keyctl link @u @s", + "echo password | mkfs.bcachefs -L root --encrypted /dev/vda3", + "echo password | bcachefs unlock /dev/vda3", + "mount -t bcachefs /dev/vda3 /mnt", + "mkfs.ext3 -L boot /dev/vda1", + "mkdir -p /mnt/boot", + "mount /dev/vda1 /mnt/boot", + ) + ''; + }; + + bcachefsMulti = makeInstallerTest "bcachefs-multi" { + extraInstallerConfig = { + boot.supportedFilesystems = [ "bcachefs" ]; + }; + + createPartitions = '' + machine.succeed( + "flock /dev/vda parted --script /dev/vda -- mklabel msdos" + + " mkpart primary ext2 1M 100MB" # /boot + + " mkpart primary linux-swap 100M 1024M" # swap + + " mkpart primary 1024M 4096M" # / + + " mkpart primary 4096M -1s", # / + "udevadm settle", + "mkswap /dev/vda2 -L swap", + "swapon -L swap", + "mkfs.bcachefs -L root --metadata_replicas 2 --foreground_target ssd --promote_target ssd --background_target hdd --label ssd /dev/vda3 --label hdd /dev/vda4", + "mount -t bcachefs /dev/vda3:/dev/vda4 /mnt", + "mkfs.ext3 -L boot /dev/vda1", + "mkdir -p /mnt/boot", + "mount /dev/vda1 /mnt/boot", + ) + ''; + }; + # Test a basic install using GRUB 1. grub1 = makeInstallerTest "grub1" rec { createPartitions = '' diff --git a/nixos/tests/kanidm.nix b/nixos/tests/kanidm.nix new file mode 100644 index 0000000000000..d34f680f5224b --- /dev/null +++ b/nixos/tests/kanidm.nix @@ -0,0 +1,75 @@ +import ./make-test-python.nix ({ pkgs, ... }: + let + certs = import ./common/acme/server/snakeoil-certs.nix; + serverDomain = certs.domain; + in + { + name = "kanidm"; + meta.maintainers = with pkgs.lib.maintainers; [ erictapen Flakebi ]; + + nodes.server = { config, pkgs, lib, ... }: { + services.kanidm = { + enableServer = true; + serverSettings = { + origin = "https://${serverDomain}"; + domain = serverDomain; + bindaddress = "[::1]:8443"; + ldapbindaddress = "[::1]:636"; + }; + }; + + services.nginx = { + enable = true; + recommendedProxySettings = true; + virtualHosts."${serverDomain}" = { + forceSSL = true; + sslCertificate = certs."${serverDomain}".cert; + sslCertificateKey = certs."${serverDomain}".key; + locations."/".proxyPass = "http://[::1]:8443"; + }; + }; + + security.pki.certificateFiles = [ certs.ca.cert ]; + + networking.hosts."::1" = [ serverDomain ]; + networking.firewall.allowedTCPPorts = [ 80 443 ]; + + users.users.kanidm.shell = pkgs.bashInteractive; + + environment.systemPackages = with pkgs; [ kanidm openldap ripgrep ]; + }; + + nodes.client = { pkgs, nodes, ... }: { + services.kanidm = { + enableClient = true; + clientSettings = { + uri = "https://${serverDomain}"; + }; + }; + + networking.hosts."${nodes.server.config.networking.primaryIPAddress}" = [ serverDomain ]; + + security.pki.certificateFiles = [ certs.ca.cert ]; + }; + + testScript = { nodes, ... }: + let + ldapBaseDN = builtins.concatStringsSep "," (map (s: "dc=" + s) (pkgs.lib.splitString "." serverDomain)); + + # We need access to the config file in the test script. + filteredConfig = pkgs.lib.converge + (pkgs.lib.filterAttrsRecursive (_: v: v != null)) + nodes.server.config.services.kanidm.serverSettings; + serverConfigFile = (pkgs.formats.toml { }).generate "server.toml" filteredConfig; + + in + '' + start_all() + server.wait_for_unit("kanidm.service") + server.wait_until_succeeds("curl -sf https://${serverDomain} | grep Kanidm") + server.wait_until_succeeds("ldapsearch -H ldap://[::1]:636 -b '${ldapBaseDN}' -x '(name=test)'") + client.wait_until_succeeds("kanidm login -D anonymous && kanidm self whoami | grep anonymous@${serverDomain}") + (rv, result) = server.execute("kanidmd recover_account -d quiet -c ${serverConfigFile} -n admin 2>&1 | rg -o '[A-Za-z0-9]{48}'") + assert rv == 0 + ''; + }) diff --git a/nixos/tests/kernel-generic.nix b/nixos/tests/kernel-generic.nix index f34d5d6079404..1e60198abdd04 100644 --- a/nixos/tests/kernel-generic.nix +++ b/nixos/tests/kernel-generic.nix @@ -30,6 +30,7 @@ let linux_5_4_hardened linux_5_10_hardened linux_5_15_hardened + linux_5_17_hardened linux_testing; }; diff --git a/nixos/tests/lxd-image-server.nix b/nixos/tests/lxd-image-server.nix index fa40e33e74dd6..072f4570c2c9f 100644 --- a/nixos/tests/lxd-image-server.nix +++ b/nixos/tests/lxd-image-server.nix @@ -1,54 +1,21 @@ -import ./make-test-python.nix ({ pkgs, ...} : +import ./make-test-python.nix ({ pkgs, lib, ... } : let - # Since we don't have access to the internet during the tests, we have to - # pre-fetch lxd containers beforehand. - # - # I've chosen to import Alpine Linux, because its image is turbo-tiny and, - # generally, sufficient for our tests. - alpine-meta = pkgs.fetchurl { - url = "https://tarballs.nixos.org/alpine/3.12/lxd.tar.xz"; - hash = "sha256-1tcKaO9lOkvqfmG/7FMbfAEToAuFy2YMewS8ysBKuLA="; - }; - - alpine-rootfs = pkgs.fetchurl { - url = "https://tarballs.nixos.org/alpine/3.12/rootfs.tar.xz"; - hash = "sha256-Tba9sSoaiMtQLY45u7p5DMqXTSDgs/763L/SQp0bkCA="; + lxd-image = import ../release.nix { + configuration = { + # Building documentation makes the test unnecessarily take a longer time: + documentation.enable = lib.mkForce false; + }; }; - lxd-config = pkgs.writeText "config.yaml" '' - storage_pools: - - name: default - driver: dir - config: - source: /var/lxd-pool - - networks: - - name: lxdbr0 - type: bridge - config: - ipv4.address: auto - ipv6.address: none - - profiles: - - name: default - devices: - eth0: - name: eth0 - network: lxdbr0 - type: nic - root: - path: / - pool: default - type: disk - ''; - + lxd-image-metadata = lxd-image.lxdMeta.${pkgs.system}; + lxd-image-rootfs = lxd-image.lxdImage.${pkgs.system}; in { name = "lxd-image-server"; meta = with pkgs.lib.maintainers; { - maintainers = [ mkg20001 ]; + maintainers = [ mkg20001 patryk27 ]; }; nodes.machine = { lib, ... }: { @@ -100,20 +67,20 @@ in { # lxd expects the pool's directory to already exist machine.succeed("mkdir /var/lxd-pool") - machine.succeed( - "cat ${lxd-config} | lxd init --preseed" + "cat ${./common/lxd/config.yaml} | lxd init --preseed" ) machine.succeed( - "lxc image import ${alpine-meta} ${alpine-rootfs} --alias alpine" + "lxc image import ${lxd-image-metadata}/*/*.tar.xz ${lxd-image-rootfs}/*/*.tar.xz --alias nixos" ) - loc = "/var/www/simplestreams/images/iats/alpine/amd64/default/v1" + loc = "/var/www/simplestreams/images/iats/nixos/amd64/default/v1" with subtest("push image to server"): - machine.succeed("lxc launch alpine test") - machine.succeed("lxc stop test") + machine.succeed("lxc launch nixos test") + machine.sleep(5) + machine.succeed("lxc stop -f test") machine.succeed("lxc publish --public test --alias=testimg") machine.succeed("lxc image export testimg") machine.succeed("ls >&2") diff --git a/nixos/tests/lxd-image.nix b/nixos/tests/lxd-image.nix deleted file mode 100644 index 4930b55f19094..0000000000000 --- a/nixos/tests/lxd-image.nix +++ /dev/null @@ -1,89 +0,0 @@ -# This test ensures that the nixOS lxd images builds and functions properly -# It has been extracted from `lxd.nix` to seperate failures of just the image and the lxd software - -import ./make-test-python.nix ({ pkgs, ...} : let - release = import ../release.nix { - /* configuration = { - environment.systemPackages = with pkgs; [ stdenv ]; # inject stdenv so rebuild test works - }; */ - }; - - metadata = release.lxdMeta.${pkgs.system}; - image = release.lxdImage.${pkgs.system}; - - lxd-config = pkgs.writeText "config.yaml" '' - storage_pools: - - name: default - driver: dir - config: - source: /var/lxd-pool - - networks: - - name: lxdbr0 - type: bridge - config: - ipv4.address: auto - ipv6.address: none - - profiles: - - name: default - devices: - eth0: - name: eth0 - network: lxdbr0 - type: nic - root: - path: / - pool: default - type: disk - ''; -in { - name = "lxd-image"; - - meta = with pkgs.lib.maintainers; { - maintainers = [ mkg20001 ]; - }; - - nodes.machine = { lib, ... }: { - virtualisation = { - # disk full otherwise - diskSize = 2048; - - lxc.lxcfs.enable = true; - lxd.enable = true; - }; - }; - - testScript = '' - machine.wait_for_unit("sockets.target") - machine.wait_for_unit("lxd.service") - machine.wait_for_file("/var/lib/lxd/unix.socket") - - # It takes additional second for lxd to settle - machine.sleep(1) - - # lxd expects the pool's directory to already exist - machine.succeed("mkdir /var/lxd-pool") - - machine.succeed( - "cat ${lxd-config} | lxd init --preseed" - ) - - # TODO: test custom built container aswell - - with subtest("importing container works"): - machine.succeed("lxc image import ${metadata}/*/*.tar.xz ${image}/*/*.tar.xz --alias nixos") - - with subtest("launching container works"): - machine.succeed("lxc launch nixos machine -c security.nesting=true") - # make sure machine boots up properly - machine.sleep(5) - - with subtest("container shell works"): - machine.succeed("echo true | lxc exec machine /run/current-system/sw/bin/bash -") - machine.succeed("lxc exec machine /run/current-system/sw/bin/true") - - # with subtest("rebuilding works"): - # machine.succeed("lxc exec machine /run/current-system/sw/bin/nixos-rebuild switch") - ''; -}) diff --git a/nixos/tests/lxd.nix b/nixos/tests/lxd.nix index 162bbcc47e871..15d16564d641c 100644 --- a/nixos/tests/lxd.nix +++ b/nixos/tests/lxd.nix @@ -1,79 +1,18 @@ -import ./make-test-python.nix ({ pkgs, ...} : +import ./make-test-python.nix ({ pkgs, lib, ... } : let - # Since we don't have access to the internet during the tests, we have to - # pre-fetch lxd containers beforehand. - # - # I've chosen to import Alpine Linux, because its image is turbo-tiny and, - # generally, sufficient for our tests. - alpine-meta-x86 = pkgs.fetchurl { - url = "https://tarballs.nixos.org/alpine/3.12/lxd.tar.xz"; - hash = "sha256-1tcKaO9lOkvqfmG/7FMbfAEToAuFy2YMewS8ysBKuLA="; - }; - alpine-meta-for = arch: pkgs.stdenv.mkDerivation { - name = "alpine-meta-${arch}"; - version = "3.12"; - unpackPhase = "true"; - buildPhase = '' - runHook preBuild - - tar xvf ${alpine-meta-x86} - sed -i 's/architecture: .*/architecture: ${arch}/' metadata.yaml - - runHook postBuild - ''; - installPhase = '' - runHook preInstall - - tar czRf $out * - - runHook postInstall - ''; - }; + lxd-image = import ../release.nix { + configuration = { + # Building documentation makes the test unnecessarily take a longer time: + documentation.enable = lib.mkForce false; - alpine-meta = { - x86_64-linux = alpine-meta-x86; - aarch64-linux = alpine-meta-for "aarch64"; - }.${pkgs.system} or (throw "Unsupported system: ${pkgs.system}"); - - alpine-rootfs = { - x86_64-linux = pkgs.fetchurl { - url = "https://tarballs.nixos.org/alpine/3.12/rootfs.tar.xz"; - hash = "sha256-Tba9sSoaiMtQLY45u7p5DMqXTSDgs/763L/SQp0bkCA="; - }; - aarch64-linux = pkgs.fetchurl { - url = "https://dl-cdn.alpinelinux.org/alpine/v3.15/releases/aarch64/alpine-minirootfs-3.15.4-aarch64.tar.gz"; - hash = "sha256-9kBz8Jwmo8XepJhTMt5zilCaHHpflnUH7y9+0To39Us="; + # Our tests require `grep` & friends: + environment.systemPackages = with pkgs; [ busybox ]; }; - }.${pkgs.system} or (throw "Unsupported system: ${pkgs.system}"); - - lxd-config = pkgs.writeText "config.yaml" '' - storage_pools: - - name: default - driver: dir - config: - source: /var/lxd-pool - - networks: - - name: lxdbr0 - type: bridge - config: - ipv4.address: auto - ipv6.address: none - - profiles: - - name: default - devices: - eth0: - name: eth0 - network: lxdbr0 - type: nic - root: - path: / - pool: default - type: disk - ''; + }; + lxd-image-metadata = lxd-image.lxdMeta.${pkgs.system}; + lxd-image-rootfs = lxd-image.lxdImage.${pkgs.system}; in { name = "lxd"; @@ -84,6 +23,8 @@ in { nodes.machine = { lib, ... }: { virtualisation = { + diskSize = 2048; + # Since we're testing `limits.cpu`, we've gotta have a known number of # cores to lean on cores = 2; @@ -108,61 +49,66 @@ in { machine.succeed("mkdir /var/lxd-pool") machine.succeed( - "cat ${lxd-config} | lxd init --preseed" + "cat ${./common/lxd/config.yaml} | lxd init --preseed" ) machine.succeed( - "lxc image import ${alpine-meta} ${alpine-rootfs} --alias alpine" + "lxc image import ${lxd-image-metadata}/*/*.tar.xz ${lxd-image-rootfs}/*/*.tar.xz --alias nixos" ) - with subtest("Containers can be launched and destroyed"): - machine.succeed("lxc launch alpine test") - machine.succeed("lxc exec test true") - machine.succeed("lxc delete -f test") + with subtest("Container can be managed"): + machine.succeed("lxc launch nixos container") + machine.sleep(5) + machine.succeed("echo true | lxc exec container /run/current-system/sw/bin/bash -") + machine.succeed("lxc exec container true") + machine.succeed("lxc delete -f container") - with subtest("Containers are being mounted with lxcfs inside"): - machine.succeed("lxc launch alpine test") + with subtest("Container is mounted with lxcfs inside"): + machine.succeed("lxc launch nixos container") + machine.sleep(5) ## ---------- ## ## limits.cpu ## - machine.succeed("lxc config set test limits.cpu 1") - machine.succeed("lxc restart test") + machine.succeed("lxc config set container limits.cpu 1") + machine.succeed("lxc restart container") + machine.sleep(5) - # Since Alpine doesn't have `nproc` pre-installed, we've gotta resort - # to the primal methods assert ( "1" - == machine.succeed("lxc exec test grep -- -c ^processor /proc/cpuinfo").strip() + == machine.succeed("lxc exec container grep -- -c ^processor /proc/cpuinfo").strip() ) - machine.succeed("lxc config set test limits.cpu 2") - machine.succeed("lxc restart test") + machine.succeed("lxc config set container limits.cpu 2") + machine.succeed("lxc restart container") + machine.sleep(5) assert ( "2" - == machine.succeed("lxc exec test grep -- -c ^processor /proc/cpuinfo").strip() + == machine.succeed("lxc exec container grep -- -c ^processor /proc/cpuinfo").strip() ) ## ------------- ## ## limits.memory ## - machine.succeed("lxc config set test limits.memory 64MB") - machine.succeed("lxc restart test") + machine.succeed("lxc config set container limits.memory 64MB") + machine.succeed("lxc restart container") + machine.sleep(5) assert ( "MemTotal: 62500 kB" - == machine.succeed("lxc exec test grep -- MemTotal /proc/meminfo").strip() + == machine.succeed("lxc exec container grep -- MemTotal /proc/meminfo").strip() ) - machine.succeed("lxc config set test limits.memory 128MB") - machine.succeed("lxc restart test") + machine.succeed("lxc config set container limits.memory 128MB") + machine.succeed("lxc restart container") + machine.sleep(5) assert ( "MemTotal: 125000 kB" - == machine.succeed("lxc exec test grep -- MemTotal /proc/meminfo").strip() + == machine.succeed("lxc exec container grep -- MemTotal /proc/meminfo").strip() ) - machine.succeed("lxc delete -f test") + machine.succeed("lxc delete -f container") ''; }) diff --git a/nixos/tests/matrix-appservice-irc.nix b/nixos/tests/matrix-appservice-irc.nix index d1c561f95dbf2..70d4585239865 100644 --- a/nixos/tests/matrix-appservice-irc.nix +++ b/nixos/tests/matrix-appservice-irc.nix @@ -20,6 +20,9 @@ import ./make-test-python.nix ({ pkgs, ... }: enable_registration = true; + # don't use this in production, always use some form of verification + enable_registration_without_verification = true; + listeners = [ { # The default but tls=false bind_addresses = [ diff --git a/nixos/tests/networking.nix b/nixos/tests/networking.nix index a1150097a0917..2cc1e9b0942ca 100644 --- a/nixos/tests/networking.nix +++ b/nixos/tests/networking.nix @@ -139,6 +139,26 @@ let client.wait_until_succeeds("ping -c 1 192.168.3.1") ''; }; + dhcpDefault = { + name = "useDHCP-by-default"; + nodes.router = router; + nodes.client = { lib, ... }: { + # Disable test driver default config + networking.interfaces = lib.mkForce {}; + networking.useNetworkd = networkd; + virtualisation.vlans = [ 1 ]; + }; + testScript = '' + start_all() + client.wait_for_unit("multi-user.target") + client.wait_until_succeeds("ip addr show dev eth1 | grep '192.168.1'") + client.shell_interact() + client.succeed("ping -c 1 192.168.1.1") + router.succeed("ping -c 1 192.168.1.1") + router.succeed("ping -c 1 192.168.1.2") + client.succeed("ping -c 1 192.168.1.2") + ''; + }; dhcpSimple = { name = "SimpleDHCP"; nodes.router = router; diff --git a/nixos/tests/openssh.nix b/nixos/tests/openssh.nix index 003813379e697..4083f5906d79a 100644 --- a/nixos/tests/openssh.nix +++ b/nixos/tests/openssh.nix @@ -80,17 +80,21 @@ in { client.wait_for_unit("network.target") client.succeed( - "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no server 'echo hello world' >&2" + "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no server 'echo hello world' >&2", + timeout=30 ) client.succeed( - "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no server 'ulimit -l' | grep 1024" + "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no server 'ulimit -l' | grep 1024", + timeout=30 ) client.succeed( - "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no server_lazy 'echo hello world' >&2" + "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no server_lazy 'echo hello world' >&2", + timeout=30 ) client.succeed( - "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no server_lazy 'ulimit -l' | grep 1024" + "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no server_lazy 'ulimit -l' | grep 1024", + timeout=30 ) with subtest("configured-authkey"): @@ -99,10 +103,12 @@ in { ) client.succeed("chmod 600 privkey.snakeoil") client.succeed( - "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i privkey.snakeoil server true" + "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i privkey.snakeoil server true", + timeout=30 ) client.succeed( - "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i privkey.snakeoil server_lazy true" + "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i privkey.snakeoil server_lazy true", + timeout=30 ) with subtest("localhost-only"): diff --git a/nixos/tests/pam/pam-oath-login.nix b/nixos/tests/pam/pam-oath-login.nix index 8fb7553de9073..c532e81e674d7 100644 --- a/nixos/tests/pam/pam-oath-login.nix +++ b/nixos/tests/pam/pam-oath-login.nix @@ -7,7 +7,7 @@ let # how many passwords have been made. In this env, we'll always be on # the 0th counter, so the password is static. # - # Generated in nix-shell -p oathToolkit + # Generated in nix-shell -p oath-toolkit # via: oathtool -v -d6 -w10 cdd4083ef8ff1fa9178c6d46bfb1a3 # and picking a the first 4: oathSnakeOilPassword1 = "143349"; diff --git a/nixos/tests/pgadmin4.nix b/nixos/tests/pgadmin4.nix index 2f6dc3bd569ff..b30299d307eb9 100644 --- a/nixos/tests/pgadmin4.nix +++ b/nixos/tests/pgadmin4.nix @@ -1,53 +1,27 @@ -import ./make-test-python.nix ({ pkgs, lib, ... }: +import ./make-test-python.nix ({ pkgs, lib, buildDeps ? [ ], pythonEnv ? [ ], ... }: + + /* + This test suite replaces the typical pytestCheckHook function in python + packages. Pgadmin4 test suite needs a running and configured postgresql + server. This is why this test exists. + + To not repeat all the python dependencies needed, this test is called directly + from the pgadmin4 derivation, which also passes the currently + used propagatedBuildInputs and any python overrides. + + Unfortunately, there doesn't seem to be an easy way to otherwise include + the needed packages here. + + Due the the needed parameters a direct call to "nixosTests.pgadmin4" fails + and needs to be called as "pgadmin4.tests" + + */ let pgadmin4SrcDir = "/pgadmin"; pgadmin4Dir = "/var/lib/pgadmin"; pgadmin4LogDir = "/var/log/pgadmin"; - python-with-needed-packages = pkgs.python3.withPackages (ps: with ps; [ - selenium - testtools - testscenarios - flask - flask-babelex - flask-babel - flask-gravatar - flask_login - flask_mail - flask_migrate - flask_sqlalchemy - flask_wtf - flask-compress - passlib - pytz - simplejson - six - sqlparse - wtforms - flask-paranoid - psutil - psycopg2 - python-dateutil - sqlalchemy - itsdangerous - flask-security-too - bcrypt - cryptography - sshtunnel - ldap3 - gssapi - flask-socketio - eventlet - httpagentparser - user-agents - wheel - authlib - qrcode - pillow - pyotp - boto3 - ]); in { name = "pgadmin4"; @@ -55,12 +29,27 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: nodes.machine = { pkgs, ... }: { imports = [ ./common/x11.nix ]; + # needed because pgadmin 6.8 will fail, if those dependencies get updated + nixpkgs.overlays = [ + (self: super: { + pythonPackages = pythonEnv; + }) + ]; + environment.systemPackages = with pkgs; [ pgadmin4 postgresql - python-with-needed-packages chromedriver chromium + # include the same packages as in pgadmin minus speaklater3 + (python3.withPackages + (ps: buildDeps ++ + [ + # test suite package requirements + pythonPackages.testscenarios + pythonPackages.selenium + ]) + ) ]; services.postgresql = { enable = true; @@ -121,7 +110,7 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: with subtest("run browser test"): machine.succeed( 'cd ${pgadmin4SrcDir}/pgadmin4-${pkgs.pgadmin4.version}/web \ - && ${python-with-needed-packages.interpreter} regression/runtests.py --pkg browser --exclude \ + && python regression/runtests.py --pkg browser --exclude \ browser.tests.test_ldap_login.LDAPLoginTestCase,browser.tests.test_ldap_login' ) @@ -131,13 +120,13 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: machine.succeed( 'cd ${pgadmin4SrcDir}/pgadmin4-${pkgs.pgadmin4.version}/web \ && export FONTCONFIG_FILE=${pkgs.makeFontsConf { fontDirectories = [];}} \ - && ${python-with-needed-packages.interpreter} regression/runtests.py --pkg feature_tests' + && python regression/runtests.py --pkg feature_tests' ) with subtest("run resql test"): machine.succeed( 'cd ${pgadmin4SrcDir}/pgadmin4-${pkgs.pgadmin4.version}/web \ - && ${python-with-needed-packages.interpreter} regression/runtests.py --pkg resql' + && python regression/runtests.py --pkg resql' ) ''; }) diff --git a/nixos/tests/pleroma.nix b/nixos/tests/pleroma.nix index 90a9a25110447..8998716243a25 100644 --- a/nixos/tests/pleroma.nix +++ b/nixos/tests/pleroma.nix @@ -158,7 +158,9 @@ import ./make-test-python.nix ({ pkgs, ... }: # Waiting for pleroma to be up. timeout 5m bash -c 'while [[ "$(curl -s -o /dev/null -w '%{http_code}' https://pleroma.nixos.test/api/v1/instance)" != "200" ]]; do sleep 2; done' - pleroma_ctl user new jamy jamy@nixos.test --password 'jamy-password' --moderator --admin -y + # Toremove the RELEASE_COOKIE bit when https://github.com/NixOS/nixpkgs/issues/166229 gets fixed. + RELEASE_COOKIE="/var/lib/pleroma/.cookie" \ + pleroma_ctl user new jamy jamy@nixos.test --password 'jamy-password' --moderator --admin -y ''; tls-cert = pkgs.runCommand "selfSignedCerts" { buildInputs = [ pkgs.openssl ]; } '' diff --git a/nixos/tests/public-inbox.nix b/nixos/tests/public-inbox.nix new file mode 100644 index 0000000000000..7de40400fcbf5 --- /dev/null +++ b/nixos/tests/public-inbox.nix @@ -0,0 +1,227 @@ +import ./make-test-python.nix ({ pkgs, lib, ... }: +let + orga = "example"; + domain = "${orga}.localdomain"; + + tls-cert = pkgs.runCommand "selfSignedCert" { buildInputs = [ pkgs.openssl ]; } '' + openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -nodes -days 36500 \ + -subj '/CN=machine.${domain}' + install -D -t $out key.pem cert.pem + ''; +in +{ + name = "public-inbox"; + + meta.maintainers = with pkgs.lib.maintainers; [ julm ]; + + machine = { config, pkgs, nodes, ... }: let + inherit (config.services) gitolite public-inbox; + # Git repositories paths in Gitolite. + # Only their baseNameOf is used for configuring public-inbox. + repositories = [ + "user/repo1" + "user/repo2" + ]; + in + { + virtualisation.diskSize = 1 * 1024; + virtualisation.memorySize = 1 * 1024; + networking.domain = domain; + + security.pki.certificateFiles = [ "${tls-cert}/cert.pem" ]; + # If using security.acme: + #security.acme.certs."${domain}".postRun = '' + # systemctl try-restart public-inbox-nntpd public-inbox-imapd + #''; + + services.public-inbox = { + enable = true; + postfix.enable = true; + openFirewall = true; + settings.publicinbox = { + css = [ "href=https://machine.${domain}/style/light.css" ]; + nntpserver = [ "nntps://machine.${domain}" ]; + wwwlisting = "match=domain"; + }; + mda = { + enable = true; + args = [ "--no-precheck" ]; # Allow Bcc: + }; + http = { + enable = true; + port = "/run/public-inbox-http.sock"; + #port = 8080; + args = ["-W0"]; + mounts = [ + "https://machine.${domain}/inbox" + ]; + }; + nntp = { + enable = true; + #port = 563; + args = ["-W0"]; + cert = "${tls-cert}/cert.pem"; + key = "${tls-cert}/key.pem"; + }; + imap = { + enable = true; + #port = 993; + args = ["-W0"]; + cert = "${tls-cert}/cert.pem"; + key = "${tls-cert}/key.pem"; + }; + inboxes = lib.recursiveUpdate (lib.genAttrs (map baseNameOf repositories) (repo: { + address = [ + # Routed to the "public-inbox:" transport in services.postfix.transport + "${repo}@${domain}" + ]; + description = '' + ${repo}@${domain} : + discussions about ${repo}. + ''; + url = "https://machine.${domain}/inbox/${repo}"; + newsgroup = "inbox.comp.${orga}.${repo}"; + coderepo = [ repo ]; + })) + { + repo2 = { + hide = [ + "imap" # FIXME: doesn't work for IMAP as of public-inbox 1.6.1 + "manifest" + "www" + ]; + }; + }; + settings.coderepo = lib.listToAttrs (map (path: lib.nameValuePair (baseNameOf path) { + dir = "/var/lib/gitolite/repositories/${path}.git"; + cgitUrl = "https://git.${domain}/${path}.git"; + }) repositories); + }; + + # Use gitolite to store Git repositories listed in coderepo entries + services.gitolite = { + enable = true; + adminPubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJmoTOQnGqX+//us5oye8UuE+tQBx9QEM7PN13jrwgqY root@localhost"; + }; + systemd.services.public-inbox-httpd = { + serviceConfig.SupplementaryGroups = [ gitolite.group ]; + }; + + # Use nginx as a reverse proxy for public-inbox-httpd + services.nginx = { + enable = true; + recommendedGzipSettings = true; + recommendedOptimisation = true; + recommendedTlsSettings = true; + recommendedProxySettings = true; + virtualHosts."machine.${domain}" = { + forceSSL = true; + sslCertificate = "${tls-cert}/cert.pem"; + sslCertificateKey = "${tls-cert}/key.pem"; + locations."/".return = "302 /inbox"; + locations."= /inbox".return = "302 /inbox/"; + locations."/inbox".proxyPass = "http://unix:${public-inbox.http.port}:/inbox"; + # If using TCP instead of a Unix socket: + #locations."/inbox".proxyPass = "http://127.0.0.1:${toString public-inbox.http.port}/inbox"; + # Referred to by settings.publicinbox.css + # See http://public-inbox.org/meta/_/text/color/ + locations."= /style/light.css".alias = pkgs.writeText "light.css" '' + * { background:#fff; color:#000 } + + a { color:#00f; text-decoration:none } + a:visited { color:#808 } + + *.q { color:#008 } + + *.add { color:#060 } + *.del {color:#900 } + *.head { color:#000 } + *.hunk { color:#960 } + + .hl.num { color:#f30 } /* number */ + .hl.esc { color:#f0f } /* escape character */ + .hl.str { color:#f30 } /* string */ + .hl.ppc { color:#c3c } /* preprocessor */ + .hl.pps { color:#f30 } /* preprocessor string */ + .hl.slc { color:#099 } /* single-line comment */ + .hl.com { color:#099 } /* multi-line comment */ + /* .hl.opt { color:#ccc } */ /* operator */ + /* .hl.ipl { color:#ccc } */ /* interpolation */ + + /* keyword groups kw[a-z] */ + .hl.kwa { color:#f90 } + .hl.kwb { color:#060 } + .hl.kwc { color:#f90 } + /* .hl.kwd { color:#ccc } */ + ''; + }; + }; + + services.postfix = { + enable = true; + setSendmail = true; + #sslCert = "${tls-cert}/cert.pem"; + #sslKey = "${tls-cert}/key.pem"; + recipientDelimiter = "+"; + }; + + environment.systemPackages = [ + pkgs.mailutils + pkgs.openssl + ]; + + }; + + testScript = '' + start_all() + machine.wait_for_unit("multi-user.target") + machine.wait_for_unit("public-inbox-init.service") + + # Very basic check that Gitolite can work; + # Gitolite is not needed for the rest of this testScript + machine.wait_for_unit("gitolite-init.service") + + # List inboxes through public-inbox-httpd + machine.wait_for_unit("nginx.service") + machine.succeed("curl -L https://machine.${domain} | grep repo1@${domain}") + # The repo2 inbox is hidden + machine.fail("curl -L https://machine.${domain} | grep repo2@${domain}") + machine.wait_for_unit("public-inbox-httpd.service") + + # Send a mail and read it through public-inbox-httpd + # Must work too when using a recipientDelimiter. + machine.wait_for_unit("postfix.service") + machine.succeed("mail -t <${pkgs.writeText "mail" '' + Subject: Testing mail + From: root@localhost + To: repo1+extension@${domain} + Message-ID: <repo1@root-1> + Content-Type: text/plain; charset=utf-8 + Content-Disposition: inline + + This is a testing mail. + ''}") + machine.sleep(5) + machine.succeed("curl -L 'https://machine.${domain}/inbox/repo1/repo1@root-1/T/#u' | grep 'This is a testing mail.'") + + # Read a mail through public-inbox-imapd + machine.wait_for_open_port(993) + machine.wait_for_unit("public-inbox-imapd.service") + machine.succeed("openssl s_client -ign_eof -crlf -connect machine.${domain}:993 <${pkgs.writeText "imap-commands" '' + tag login anonymous@${domain} anonymous + tag SELECT INBOX.comp.${orga}.repo1.0 + tag FETCH 1 (BODY[HEADER]) + tag LOGOUT + ''} | grep '^Message-ID: <repo1@root-1>'") + + # TODO: Read a mail through public-inbox-nntpd + #machine.wait_for_open_port(563) + #machine.wait_for_unit("public-inbox-nntpd.service") + + # Delete a mail. + # Note that the use of an extension not listed in the addresses + # require to use --all + machine.succeed("curl -L https://machine.example.localdomain/inbox/repo1/repo1@root-1/raw | sudo -u public-inbox public-inbox-learn rm --all") + machine.fail("curl -L https://machine.example.localdomain/inbox/repo1/repo1@root-1/T/#u | grep 'This is a testing mail.'") + ''; +}) diff --git a/nixos/tests/systemd-nspawn.nix b/nixos/tests/systemd-nspawn.nix index 5bf55060d2e03..c2cb92d11301c 100644 --- a/nixos/tests/systemd-nspawn.nix +++ b/nixos/tests/systemd-nspawn.nix @@ -25,8 +25,15 @@ let nspawnImages = (pkgs.runCommand "localhost" { buildInputs = [ pkgs.coreutils pkgs.gnupg ]; } '' mkdir -p $out cd $out + + # produce a testimage.raw dd if=/dev/urandom of=$out/testimage.raw bs=$((1024*1024+7)) count=5 - sha256sum testimage.raw > SHA256SUMS + + # produce a testimage2.tar.xz, containing the hello store path + tar cvJpf testimage2.tar.xz ${pkgs.hello} + + # produce signature(s) + sha256sum testimage* > SHA256SUMS export GNUPGHOME="$(mktemp -d)" cp -R ${gpgKeyring}/* $GNUPGHOME gpg --batch --sign --detach-sign --output SHA256SUMS.gpg SHA256SUMS @@ -56,5 +63,9 @@ in { client.succeed( "cmp /var/lib/machines/testimage.raw ${nspawnImages}/testimage.raw" ) + client.succeed("machinectl pull-tar --verify=signature http://server/testimage2.tar.xz") + client.succeed( + "cmp /var/lib/machines/testimage2/${pkgs.hello}/bin/hello ${pkgs.hello}/bin/hello" + ) ''; }) diff --git a/nixos/tests/virtualbox.nix b/nixos/tests/virtualbox.nix index 27093aab96ee5..4eb402a7d36ef 100644 --- a/nixos/tests/virtualbox.nix +++ b/nixos/tests/virtualbox.nix @@ -222,7 +222,7 @@ let machine.execute(ru("VBoxManage controlvm ${name} poweroff")) machine.succeed("rm -rf ${sharePath}") machine.succeed("mkdir -p ${sharePath}") - machine.succeed("chown alice.users ${sharePath}") + machine.succeed("chown alice:users ${sharePath}") def create_vm_${name}(): |