diff options
Diffstat (limited to 'nixos')
55 files changed, 1576 insertions, 482 deletions
diff --git a/nixos/doc/manual/development/linking-nixos-tests-to-packages.section.md b/nixos/doc/manual/development/linking-nixos-tests-to-packages.section.md new file mode 100644 index 00000000000..38a64027f7c --- /dev/null +++ b/nixos/doc/manual/development/linking-nixos-tests-to-packages.section.md @@ -0,0 +1,6 @@ +# Linking NixOS tests to packages {#sec-linking-nixos-tests-to-packages} + +You can link NixOS module tests to the packages that they exercised, +so that the tests can be run automatically during code review when the package gets changed. +This is +[described in the nixpkgs manual](https://nixos.org/manual/nixpkgs/stable/#ssec-nixos-tests-linking). diff --git a/nixos/doc/manual/development/nixos-tests.xml b/nixos/doc/manual/development/nixos-tests.xml index 702fc03f668..67dc09fc715 100644 --- a/nixos/doc/manual/development/nixos-tests.xml +++ b/nixos/doc/manual/development/nixos-tests.xml @@ -16,4 +16,5 @@ xlink:href="https://github.com/NixOS/nixpkgs/tree/master/nixos/tests">nixos/test <xi:include href="../from_md/development/writing-nixos-tests.section.xml" /> <xi:include href="../from_md/development/running-nixos-tests.section.xml" /> <xi:include href="../from_md/development/running-nixos-tests-interactively.section.xml" /> + <xi:include href="../from_md/development/linking-nixos-tests-to-packages.section.xml" /> </chapter> diff --git a/nixos/doc/manual/from_md/development/linking-nixos-tests-to-packages.section.xml b/nixos/doc/manual/from_md/development/linking-nixos-tests-to-packages.section.xml new file mode 100644 index 00000000000..666bbec6162 --- /dev/null +++ b/nixos/doc/manual/from_md/development/linking-nixos-tests-to-packages.section.xml @@ -0,0 +1,10 @@ +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="sec-linking-nixos-tests-to-packages"> + <title>Linking NixOS tests to packages</title> + <para> + You can link NixOS module tests to the packages that they exercised, + so that the tests can be run automatically during code review when + the package gets changed. This is + <link xlink:href="https://nixos.org/manual/nixpkgs/stable/#ssec-nixos-tests-linking">described + in the nixpkgs manual</link>. + </para> +</section> 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 9dd98a5262f..2874039e3f0 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 @@ -125,6 +125,30 @@ <link linkend="opt-services.prometheus.exporters.buildkite-agent.enable">services.prometheus.exporters.buildkite-agent</link>. </para> </listitem> + <listitem> + <para> + <link xlink:href="https://github.com/prometheus/influxdb_exporter">influxdb-exporter</link> + a Prometheus exporter that exports metrics received on an + InfluxDB compatible endpoint is now available as + <link linkend="opt-services.prometheus.exporters.influxdb.enable">services.prometheus.exporters.influxdb</link>. + </para> + </listitem> + <listitem> + <para> + <link xlink:href="https://github.com/matrix-discord/mx-puppet-discord">mx-puppet-discord</link>, + a discord puppeting bridge for matrix. Available as + <link linkend="opt-services.mx-puppet-discord.enable">services.mx-puppet-discord</link>. + </para> + </listitem> + <listitem> + <para> + <link xlink:href="https://www.meshcommander.com/meshcentral2/overview">MeshCentral</link>, + a remote administration service (<quote>TeamViewer but + self-hosted and with more features</quote>) is now available + with a package and a module: + <link linkend="opt-services.meshcentral.enable">services.meshcentral.enable</link> + </para> + </listitem> </itemizedlist> </section> <section xml:id="sec-release-21.11-incompatibilities"> @@ -555,6 +579,51 @@ 6.0.0 to 9.0.0 </para> </listitem> + <listitem> + <para> + <literal>tt-rss</literal> was upgraded to the commit on + 2021-06-21, which has breaking changes. If you use + <literal>services.tt-rss.extraConfig</literal> you should + migrate to the <literal>putenv</literal>-style configuration. + See + <link xlink:href="https://community.tt-rss.org/t/rip-config-php-hello-classes-config-php/4337">this + Discourse post</link> in the tt-rss forums for more details. + </para> + </listitem> + <listitem> + <para> + The following Visual Studio Code extensions were renamed to + keep the naming convention uniform. + </para> + <itemizedlist spacing="compact"> + <listitem> + <para> + <literal>bbenoist.Nix</literal> -> + <literal>bbenoist.nix</literal> + </para> + </listitem> + <listitem> + <para> + <literal>CoenraadS.bracket-pair-colorizer</literal> -> + <literal>coenraads.bracket-pair-colorizer</literal> + </para> + </listitem> + <listitem> + <para> + <literal>golang.Go</literal> -> + <literal>golang.go</literal> + </para> + </listitem> + </itemizedlist> + </listitem> + <listitem> + <para> + <literal>services.uptimed</literal> now uses + <literal>/var/lib/uptimed</literal> as its stateDirectory + instead of <literal>/var/spool/uptimed</literal>. Make sure to + move all files to the new directory. + </para> + </listitem> </itemizedlist> </section> <section xml:id="sec-release-21.11-notable-changes"> diff --git a/nixos/doc/manual/release-notes/rl-2111.section.md b/nixos/doc/manual/release-notes/rl-2111.section.md index 12e1a938433..fe7a7326a40 100644 --- a/nixos/doc/manual/release-notes/rl-2111.section.md +++ b/nixos/doc/manual/release-notes/rl-2111.section.md @@ -39,6 +39,12 @@ pt-services.clipcat.enable). - [buildkite-agent-metrics](https://github.com/buildkite/buildkite-agent-metrics), a command-line tool for collecting Buildkite agent metrics, now has a Prometheus exporter available as [services.prometheus.exporters.buildkite-agent](#opt-services.prometheus.exporters.buildkite-agent.enable). +- [influxdb-exporter](https://github.com/prometheus/influxdb_exporter) a Prometheus exporter that exports metrics received on an InfluxDB compatible endpoint is now available as [services.prometheus.exporters.influxdb](#opt-services.prometheus.exporters.influxdb.enable). + +- [mx-puppet-discord](https://github.com/matrix-discord/mx-puppet-discord), a discord puppeting bridge for matrix. Available as [services.mx-puppet-discord](#opt-services.mx-puppet-discord.enable). + +- [MeshCentral](https://www.meshcommander.com/meshcentral2/overview), a remote administration service ("TeamViewer but self-hosted and with more features") is now available with a package and a module: [services.meshcentral.enable](#opt-services.meshcentral.enable) + ## Backward Incompatibilities {#sec-release-21.11-incompatibilities} - The `staticjinja` package has been upgraded from 1.0.4 to 3.0.1 @@ -142,6 +148,15 @@ pt-services.clipcat.enable). - the `mingw-64` package has been upgraded from 6.0.0 to 9.0.0 +- `tt-rss` was upgraded to the commit on 2021-06-21, which has breaking changes. If you use `services.tt-rss.extraConfig` you should migrate to the `putenv`-style configuration. See [this Discourse post](https://community.tt-rss.org/t/rip-config-php-hello-classes-config-php/4337) in the tt-rss forums for more details. + +- The following Visual Studio Code extensions were renamed to keep the naming convention uniform. + - `bbenoist.Nix` -> `bbenoist.nix` + - `CoenraadS.bracket-pair-colorizer` -> `coenraads.bracket-pair-colorizer` + - `golang.Go` -> `golang.go` + +- `services.uptimed` now uses `/var/lib/uptimed` as its stateDirectory instead of `/var/spool/uptimed`. Make sure to move all files to the new directory. + ## Other Notable Changes {#sec-release-21.11-notable-changes} - The setting [`services.openssh.logLevel`](options.html#opt-services.openssh.logLevel) `"VERBOSE"` `"INFO"`. This brings NixOS in line with upstream and other Linux distributions, and reduces log spam on servers due to bruteforcing botnets. diff --git a/nixos/lib/testing-python.nix b/nixos/lib/testing-python.nix index 768f1dc2a17..c703484666d 100644 --- a/nixos/lib/testing-python.nix +++ b/nixos/lib/testing-python.nix @@ -130,9 +130,12 @@ rec { nodeHostNames = map (c: c.config.system.name) (lib.attrValues nodes); + # TODO: This is an implementation error and needs fixing + # the testing famework cannot legitimately restrict hostnames further + # beyond RFC1035 invalidNodeNames = lib.filter (node: builtins.match "^[A-z_]([A-z0-9_]+)?$" node == null) - (builtins.attrNames nodes); + nodeHostNames; testScript' = # Call the test script with the computed nodes. @@ -146,7 +149,9 @@ rec { Cannot create machines out of (${lib.concatStringsSep ", " invalidNodeNames})! All machines are referenced as python variables in the testing framework which will break the script when special characters are used. - Please stick to alphanumeric chars and underscores as separation. + + This is an IMPLEMENTATION ERROR and needs to be fixed. Meanwhile, + please stick to alphanumeric chars and underscores as separation. '' else lib.warnIf skipLint "Linting is disabled" (runCommand testDriverName { diff --git a/nixos/modules/hardware/all-firmware.nix b/nixos/modules/hardware/all-firmware.nix index 3e88a4c20ad..a4e4fa8d0ed 100644 --- a/nixos/modules/hardware/all-firmware.nix +++ b/nixos/modules/hardware/all-firmware.nix @@ -35,6 +35,14 @@ in { ''; }; + hardware.wirelessRegulatoryDatabase = mkOption { + default = false; + type = types.bool; + description = '' + Load the wireless regulatory database at boot. + ''; + }; + }; @@ -50,6 +58,7 @@ in { rtl8723bs-firmware rtl8761b-firmware rtw88-firmware + rtw89-firmware zd1211fw alsa-firmware sof-firmware @@ -58,6 +67,7 @@ in { ++ optionals (versionOlder config.boot.kernelPackages.kernel.version "4.13") [ rtl8723bs-firmware ]; + hardware.wirelessRegulatoryDatabase = true; }) (mkIf cfg.enableAllFirmware { assertions = [{ @@ -75,5 +85,8 @@ in { b43FirmwareCutter ] ++ optional (pkgs.stdenv.hostPlatform.isi686 || pkgs.stdenv.hostPlatform.isx86_64) facetimehd-firmware; }) + (mkIf cfg.wirelessRegulatoryDatabase { + hardware.firmware = [ pkgs.wireless-regdb ]; + }) ]; } diff --git a/nixos/modules/installer/cd-dvd/iso-image.nix b/nixos/modules/installer/cd-dvd/iso-image.nix index d94af0b5bf7..f0384513247 100644 --- a/nixos/modules/installer/cd-dvd/iso-image.nix +++ b/nixos/modules/installer/cd-dvd/iso-image.nix @@ -654,7 +654,11 @@ in ]; fileSystems."/" = - { fsType = "tmpfs"; + # This module is often over-layed onto an existing host config + # that defines `/`. We use mkOverride 60 to override standard + # values, but at the same time leave room for mkForce values + # targeted at the image build. + { fsType = mkOverride 60 "tmpfs"; options = [ "mode=0755" ]; }; diff --git a/nixos/modules/installer/netboot/netboot.nix b/nixos/modules/installer/netboot/netboot.nix index 238ab6d0617..f7543fdf4a2 100644 --- a/nixos/modules/installer/netboot/netboot.nix +++ b/nixos/modules/installer/netboot/netboot.nix @@ -30,7 +30,11 @@ with lib; else [ pkgs.grub2 pkgs.syslinux ]); fileSystems."/" = - { fsType = "tmpfs"; + # This module is often over-layed onto an existing host config + # that defines `/`. We use mkOverride 60 to override standard + # values, but at the same time leave room for mkForce values + # targeted at the image build. + { fsType = mkOverride 60 "tmpfs"; options = [ "mode=0755" ]; }; diff --git a/nixos/modules/installer/tools/nix-fallback-paths.nix b/nixos/modules/installer/tools/nix-fallback-paths.nix index e3576074a5b..15c76287e34 100644 --- a/nixos/modules/installer/tools/nix-fallback-paths.nix +++ b/nixos/modules/installer/tools/nix-fallback-paths.nix @@ -1,7 +1,7 @@ { - x86_64-linux = "/nix/store/qsgz2hhn6mzlzp53a7pwf9z2pq3l5z6h-nix-2.3.14"; - i686-linux = "/nix/store/1yw40bj04lykisw2jilq06lir3k9ga4a-nix-2.3.14"; - aarch64-linux = "/nix/store/32yzwmynmjxfrkb6y6l55liaqdrgkj4a-nix-2.3.14"; - x86_64-darwin = "/nix/store/06j0vi2d13w4l0p3jsigq7lk4x6gkycj-nix-2.3.14"; - aarch64-darwin = "/nix/store/77wi7vpbrghw5rgws25w30bwb8yggnk9-nix-2.3.14"; + x86_64-linux = "/nix/store/jhbxh1jwjc3hjhzs9y2hifdn0rmnfwaj-nix-2.3.15"; + i686-linux = "/nix/store/9pspwnkdrgzma1l4xlv7arhwa56y16di-nix-2.3.15"; + aarch64-linux = "/nix/store/72aqi5g7f4fhgvgafbcqwcpqjgnczj48-nix-2.3.15"; + x86_64-darwin = "/nix/store/6p6qwp73dgfkqhynmxrzbx1lcfgfpqal-nix-2.3.15"; + aarch64-darwin = "/nix/store/dmq2vksdhssgfl822shd0ky3x5x0klh4-nix-2.3.15"; } diff --git a/nixos/modules/misc/ids.nix b/nixos/modules/misc/ids.nix index 858c7ee53db..a7a31ba8576 100644 --- a/nixos/modules/misc/ids.nix +++ b/nixos/modules/misc/ids.nix @@ -187,6 +187,7 @@ in #seeks = 148; # removed 2020-06-21 prosody = 149; i2pd = 150; + systemd-coredump = 151; systemd-network = 152; systemd-resolve = 153; systemd-timesync = 154; diff --git a/nixos/modules/misc/version.nix b/nixos/modules/misc/version.nix index a6fffb76f6e..8f246a9278b 100644 --- a/nixos/modules/misc/version.nix +++ b/nixos/modules/misc/version.nix @@ -103,9 +103,10 @@ in '' NAME=NixOS ID=nixos - VERSION="${cfg.version} (${cfg.codeName})" + VERSION="${cfg.release} (${cfg.codeName})" VERSION_CODENAME=${toLower cfg.codeName} - VERSION_ID="${cfg.version}" + VERSION_ID="${cfg.release}" + BUILD_ID="${cfg.version}" PRETTY_NAME="NixOS ${cfg.release} (${cfg.codeName})" LOGO="nix-snowflake" HOME_URL="https://nixos.org/" diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 4d1700ed99a..1a6d8599e7e 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -236,6 +236,7 @@ ./security/doas.nix ./security/systemd-confinement.nix ./security/tpm2.nix + ./services/admin/meshcentral.nix ./services/admin/oxidized.nix ./services/admin/salt/master.nix ./services/admin/salt/minion.nix @@ -519,6 +520,7 @@ ./services/misc/logkeys.nix ./services/misc/leaps.nix ./services/misc/lidarr.nix + ./services/misc/libreddit.nix ./services/misc/lifecycled.nix ./services/misc/mame.nix ./services/misc/matrix-appservice-discord.nix @@ -529,6 +531,7 @@ ./services/misc/mediatomb.nix ./services/misc/metabase.nix ./services/misc/mwlib.nix + ./services/misc/mx-puppet-discord.nix ./services/misc/n8n.nix ./services/misc/nix-daemon.nix ./services/misc/nix-gc.nix @@ -633,6 +636,7 @@ ./services/network-filesystems/glusterfs.nix ./services/network-filesystems/kbfs.nix ./services/network-filesystems/ipfs.nix + ./services/network-filesystems/litestream/default.nix ./services/network-filesystems/netatalk.nix ./services/network-filesystems/nfsd.nix ./services/network-filesystems/openafs/client.nix @@ -960,6 +964,7 @@ ./services/web-apps/moodle.nix ./services/web-apps/nextcloud.nix ./services/web-apps/nexus.nix + ./services/web-apps/node-red.nix ./services/web-apps/plantuml-server.nix ./services/web-apps/plausible.nix ./services/web-apps/pgpkeyserver-lite.nix diff --git a/nixos/modules/programs/captive-browser.nix b/nixos/modules/programs/captive-browser.nix index 1f223e2475c..007b0369ec1 100644 --- a/nixos/modules/programs/captive-browser.nix +++ b/nixos/modules/programs/captive-browser.nix @@ -27,6 +27,7 @@ in browser = mkOption { type = types.str; default = concatStringsSep " " [ + ''env XDG_CONFIG_HOME="$PREV_CONFIG_HOME"'' ''${pkgs.chromium}/bin/chromium'' ''--user-data-dir=''${XDG_DATA_HOME:-$HOME/.local/share}/chromium-captive'' ''--proxy-server="socks5://$PROXY"'' @@ -111,6 +112,7 @@ in security.wrappers.captive-browser = { capabilities = "cap_net_raw+p"; source = pkgs.writeShellScript "captive-browser" '' + export PREV_CONFIG_HOME="$XDG_CONFIG_HOME" export XDG_CONFIG_HOME=${pkgs.writeTextDir "captive-browser.toml" '' browser = """${cfg.browser}""" dhcp-dns = """${cfg.dhcp-dns}""" diff --git a/nixos/modules/services/admin/meshcentral.nix b/nixos/modules/services/admin/meshcentral.nix new file mode 100644 index 00000000000..ae7b6edda7d --- /dev/null +++ b/nixos/modules/services/admin/meshcentral.nix @@ -0,0 +1,53 @@ +{ config, pkgs, lib, ... }: +let + cfg = config.services.meshcentral; + configFormat = pkgs.formats.json {}; + configFile = configFormat.generate "meshcentral-config.json" cfg.settings; +in with lib; { + options.services.meshcentral = with types; { + enable = mkEnableOption "MeshCentral computer management server"; + package = mkOption { + description = "MeshCentral package to use. Replacing this may be necessary to add dependencies for extra functionality."; + type = types.package; + default = pkgs.meshcentral; + defaultText = "pkgs.meshcentral"; + }; + settings = mkOption { + description = '' + Settings for MeshCentral. Refer to upstream documentation for details: + + <itemizedlist> + <listitem><para><link xlink:href="https://github.com/Ylianst/MeshCentral/blob/master/meshcentral-config-schema.json">JSON Schema definition</link></para></listitem> + <listitem><para><link xlink:href="https://github.com/Ylianst/MeshCentral/blob/master/sample-config.json">simple sample configuration</link></para></listitem> + <listitem><para><link xlink:href="https://github.com/Ylianst/MeshCentral/blob/master/sample-config-advanced.json">complex sample configuration</link></para></listitem> + <listitem><para><link xlink:href="https://www.meshcommander.com/meshcentral2">Old homepage) with documentation link</link></para></listitem> + </itemizedlist> + ''; + type = types.submodule { + freeformType = configFormat.type; + }; + example = { + settings = { + WANonly = true; + Cert = "meshcentral.example.com"; + TlsOffload = "10.0.0.2,fd42::2"; + Port = 4430; + }; + domains."".certUrl = "https://meshcentral.example.com/"; + }; + }; + }; + config = mkIf cfg.enable { + services.meshcentral.settings.settings.autoBackup.backupPath = lib.mkDefault "/var/lib/meshcentral/backups"; + systemd.services.meshcentral = { + wantedBy = ["multi-user.target"]; + serviceConfig = { + ExecStart = "${cfg.package}/bin/meshcentral --datapath /var/lib/meshcentral --configfile ${configFile}"; + DynamicUser = true; + StateDirectory = "meshcentral"; + CacheDirectory = "meshcentral"; + }; + }; + }; + meta.maintainers = [ maintainers.lheckemann ]; +} diff --git a/nixos/modules/services/cluster/kubernetes/addons/dns.nix b/nixos/modules/services/cluster/kubernetes/addons/dns.nix index 24d86628b21..8f937a13231 100644 --- a/nixos/modules/services/cluster/kubernetes/addons/dns.nix +++ b/nixos/modules/services/cluster/kubernetes/addons/dns.nix @@ -60,6 +60,45 @@ in { sha256 = "02r440xcdsgi137k5lmmvp0z5w5fmk8g9mysq5pnysq1wl8sj6mw"; }; }; + + corefile = mkOption { + description = '' + Custom coredns corefile configuration. + + See: <link xlink:href="https://coredns.io/manual/toc/#configuration"/>. + ''; + type = types.str; + default = '' + .:${toString ports.dns} { + errors + health :${toString ports.health} + kubernetes ${cfg.clusterDomain} in-addr.arpa ip6.arpa { + pods insecure + fallthrough in-addr.arpa ip6.arpa + } + prometheus :${toString ports.metrics} + forward . /etc/resolv.conf + cache 30 + loop + reload + loadbalance + }''; + defaultText = '' + .:${toString ports.dns} { + errors + health :${toString ports.health} + kubernetes ''${config.services.kubernetes.addons.dns.clusterDomain} in-addr.arpa ip6.arpa { + pods insecure + fallthrough in-addr.arpa ip6.arpa + } + prometheus :${toString ports.metrics} + forward . /etc/resolv.conf + cache 30 + loop + reload + loadbalance + }''; + }; }; config = mkIf cfg.enable { @@ -151,20 +190,7 @@ in { namespace = "kube-system"; }; data = { - Corefile = ".:${toString ports.dns} { - errors - health :${toString ports.health} - kubernetes ${cfg.clusterDomain} in-addr.arpa ip6.arpa { - pods insecure - fallthrough in-addr.arpa ip6.arpa - } - prometheus :${toString ports.metrics} - forward . /etc/resolv.conf - cache 30 - loop - reload - loadbalance - }"; + Corefile = cfg.corefile; }; }; diff --git a/nixos/modules/services/cluster/kubernetes/pki.nix b/nixos/modules/services/cluster/kubernetes/pki.nix index d9311d3e3a0..faf951d8157 100644 --- a/nixos/modules/services/cluster/kubernetes/pki.nix +++ b/nixos/modules/services/cluster/kubernetes/pki.nix @@ -189,7 +189,7 @@ in # manually paste it in place. Just symlink. # otherwise, create the target file, ready for users to insert the token - mkdir -p $(dirname ${certmgrAPITokenPath}) + mkdir -p "$(dirname "${certmgrAPITokenPath}")" if [ -f "${cfsslAPITokenPath}" ]; then ln -fs "${cfsslAPITokenPath}" "${certmgrAPITokenPath}" else diff --git a/nixos/modules/services/continuous-integration/gitlab-runner.nix b/nixos/modules/services/continuous-integration/gitlab-runner.nix index 2c6d9530a6b..15c37c2bc76 100644 --- a/nixos/modules/services/continuous-integration/gitlab-runner.nix +++ b/nixos/modules/services/continuous-integration/gitlab-runner.nix @@ -339,6 +339,9 @@ in <literal>CI_SERVER_URL=<CI server URL></literal> <literal>REGISTRATION_TOKEN=<registration secret></literal> + + WARNING: make sure to use quoted absolute path, + or it is going to be copied to Nix Store. ''; }; registrationFlags = mkOption { @@ -523,7 +526,10 @@ in }; }; config = mkIf cfg.enable { - warnings = optional (cfg.configFile != null) "services.gitlab-runner.`configFile` is deprecated, please use services.gitlab-runner.`services`."; + warnings = (mapAttrsToList + (n: v: "services.gitlab-runner.services.${n}.`registrationConfigFile` points to a file in Nix Store. You should use quoted absolute path to prevent this.") + (filterAttrs (n: v: isStorePath v.registrationConfigFile) cfg.services)) + ++ optional (cfg.configFile != null) "services.gitlab-runner.`configFile` is deprecated, please use services.gitlab-runner.`services`."; environment.systemPackages = [ cfg.package ]; systemd.services.gitlab-runner = { description = "Gitlab Runner"; diff --git a/nixos/modules/services/databases/redis.nix b/nixos/modules/services/databases/redis.nix index 9c0740f28c9..8873f6d00e0 100644 --- a/nixos/modules/services/databases/redis.nix +++ b/nixos/modules/services/databases/redis.nix @@ -272,7 +272,7 @@ in { } (mkIf (cfg.bind != null) { bind = cfg.bind; }) (mkIf (cfg.unixSocket != null) { unixsocket = cfg.unixSocket; unixsocketperm = "${toString cfg.unixSocketPerm}"; }) - (mkIf (cfg.slaveOf != null) { slaveof = "${cfg.slaveOf.ip} ${cfg.slaveOf.port}"; }) + (mkIf (cfg.slaveOf != null) { slaveof = "${cfg.slaveOf.ip} ${toString cfg.slaveOf.port}"; }) (mkIf (cfg.masterAuth != null) { masterauth = cfg.masterAuth; }) (mkIf (cfg.requirePass != null) { requirepass = cfg.requirePass; }) ]; diff --git a/nixos/modules/services/hardware/sane.nix b/nixos/modules/services/hardware/sane.nix index 8c1bde7b415..ccf726bd182 100644 --- a/nixos/modules/services/hardware/sane.nix +++ b/nixos/modules/services/hardware/sane.nix @@ -4,7 +4,10 @@ with lib; let - pkg = pkgs.sane-backends; + pkg = pkgs.sane-backends.override { + scanSnapDriversUnfree = config.hardware.sane.drivers.scanSnap.enable; + scanSnapDriversPackage = config.hardware.sane.drivers.scanSnap.package; + }; sanedConf = pkgs.writeTextFile { name = "saned.conf"; @@ -98,6 +101,28 @@ in ''; }; + hardware.sane.drivers.scanSnap.enable = mkOption { + type = types.bool; + default = false; + example = true; + description = '' + Whether to enable drivers for the Fujitsu ScanSnap scanners. + + The driver files are unfree and extracted from the Windows driver image. + ''; + }; + + hardware.sane.drivers.scanSnap.package = mkOption { + type = types.package; + default = pkgs.sane-drivers.epjitsu; + description = '' + Epjitsu driver package to use. Useful if you want to extract the driver files yourself. + + The process is described in the <literal>/etc/sane.d/epjitsu.conf</literal> file in + the <literal>sane-backends</literal> package. + ''; + }; + services.saned.enable = mkOption { type = types.bool; default = false; diff --git a/nixos/modules/services/misc/gitea.nix b/nixos/modules/services/misc/gitea.nix index b6c1ca3e61a..2f8e595cad0 100644 --- a/nixos/modules/services/misc/gitea.nix +++ b/nixos/modules/services/misc/gitea.nix @@ -523,19 +523,12 @@ in ''} # update all hooks' binary paths - HOOKS=$(find ${cfg.repositoryRoot} -mindepth 4 -maxdepth 6 -type f -wholename "*git/hooks/*") - if [ "$HOOKS" ] - then - sed -ri 's,/nix/store/[a-z0-9.-]+/bin/gitea,${gitea}/bin/gitea,g' $HOOKS - sed -ri 's,/nix/store/[a-z0-9.-]+/bin/env,${pkgs.coreutils}/bin/env,g' $HOOKS - sed -ri 's,/nix/store/[a-z0-9.-]+/bin/bash,${pkgs.bash}/bin/bash,g' $HOOKS - sed -ri 's,/nix/store/[a-z0-9.-]+/bin/perl,${pkgs.perl}/bin/perl,g' $HOOKS - fi + ${gitea}/bin/gitea admin regenerate hooks # update command option in authorized_keys if [ -r ${cfg.stateDir}/.ssh/authorized_keys ] then - sed -ri 's,/nix/store/[a-z0-9.-]+/bin/gitea,${gitea}/bin/gitea,g' ${cfg.stateDir}/.ssh/authorized_keys + ${gitea}/bin/gitea admin regenerate keys fi ''; diff --git a/nixos/modules/services/misc/libreddit.nix b/nixos/modules/services/misc/libreddit.nix new file mode 100644 index 00000000000..77b34a85620 --- /dev/null +++ b/nixos/modules/services/misc/libreddit.nix @@ -0,0 +1,66 @@ +{ config, lib, pkgs, ... }: + +with lib; + + let + cfg = config.services.libreddit; + + args = concatStringsSep " " ([ + "--port ${toString cfg.port}" + "--address ${cfg.address}" + ] ++ optional cfg.redirect "--redirect-https"); + +in +{ + options = { + services.libreddit = { + enable = mkEnableOption "Private front-end for Reddit"; + + address = mkOption { + default = "0.0.0.0"; + example = "127.0.0.1"; + type = types.str; + description = "The address to listen on"; + }; + + port = mkOption { + default = 8080; + example = 8000; + type = types.port; + description = "The port to listen on"; + }; + + redirect = mkOption { + type = types.bool; + default = false; + description = "Enable the redirecting to HTTPS"; + }; + + openFirewall = mkOption { + type = types.bool; + default = false; + description = "Open ports in the firewall for the libreddit web interface"; + }; + + }; + }; + + config = mkIf cfg.enable { + systemd.services.libreddit = { + description = "Private front-end for Reddit"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + serviceConfig = { + DynamicUser = true; + ExecStart = "${pkgs.libreddit}/bin/libreddit ${args}"; + AmbientCapabilities = lib.mkIf (cfg.port < 1024) [ "CAP_NET_BIND_SERVICE" ]; + Restart = "on-failure"; + RestartSec = "2s"; + }; + }; + + networking.firewall = mkIf cfg.openFirewall { + allowedTCPPorts = [ cfg.port ]; + }; + }; +} diff --git a/nixos/modules/services/misc/mx-puppet-discord.nix b/nixos/modules/services/misc/mx-puppet-discord.nix new file mode 100644 index 00000000000..11116f7c348 --- /dev/null +++ b/nixos/modules/services/misc/mx-puppet-discord.nix @@ -0,0 +1,120 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let + dataDir = "/var/lib/mx-puppet-discord"; + registrationFile = "${dataDir}/discord-registration.yaml"; + cfg = config.services.mx-puppet-discord; + settingsFormat = pkgs.formats.json {}; + settingsFile = settingsFormat.generate "mx-puppet-discord-config.json" cfg.settings; + +in { + options = { + services.mx-puppet-discord = { + enable = mkEnableOption '' + mx-puppet-discord is a discord puppeting bridge for matrix. + It handles bridging private and group DMs, as well as Guilds (servers) + ''; + + settings = mkOption rec { + apply = recursiveUpdate default; + inherit (settingsFormat) type; + default = { + bridge.port = 8434; + presence = { + enabled = true; + interval = 500; + }; + provisioning.whitelist = [ ]; + relay.whitelist = [ ]; + + # variables are preceded by a colon. + namePatterns = { + user = ":name"; + userOverride = ":displayname"; + room = ":name"; + group = ":name"; + }; + + #defaults to sqlite but can be configured to use postgresql with + #connstring + database.filename = "${dataDir}/mx-puppet-discord/database.db"; + logging = { + console = "info"; + lineDateFormat = "MMM-D HH:mm:ss.SSS"; + }; + }; + example = literalExample '' + { + bridge = { + bindAddress = "localhost"; + domain = "example.com"; + homeserverUrl = "https://example.com"; + }; + + provisioning.whitelist = [ "@admin:example.com" ]; + relay.whitelist = [ "@.*:example.com" ]; + } + ''; + description = '' + <filename>config.yaml</filename> configuration as a Nix attribute set. + Configuration options should match those described in + <link xlink:href="https://github.com/matrix-discord/mx-puppet-discord/blob/master/sample.config.yaml"> + sample.config.yaml</link>. + ''; + }; + serviceDependencies = mkOption { + type = with types; listOf str; + default = optional config.services.matrix-synapse.enable "matrix-synapse.service"; + description = '' + List of Systemd services to require and wait for when starting the application service. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + systemd.services.mx-puppet-discord = { + description = '' + mx-puppet-discord is a discord puppeting bridge for matrix. + It handles bridging private and group DMs, as well as Guilds (servers). + ''; + + wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" ] ++ cfg.serviceDependencies; + after = [ "network-online.target" ] ++ cfg.serviceDependencies; + + preStart = '' + # generate the appservice's registration file if absent + if [ ! -f '${registrationFile}' ]; then + ${pkgs.mx-puppet-discord}/bin/mx-puppet-discord -r -c ${settingsFile} \ + -f ${registrationFile} + fi + ''; + + serviceConfig = { + Type = "simple"; + Restart = "always"; + + ProtectSystem = "strict"; + ProtectHome = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectControlGroups = true; + + DynamicUser = true; + PrivateTmp = true; + WorkingDirectory = pkgs.mx-puppet-discord; + StateDirectory = baseNameOf dataDir; + UMask = 0027; + + ExecStart = '' + ${pkgs.mx-puppet-discord}/bin/mx-puppet-discord -c ${settingsFile} + ''; + }; + }; + }; + + meta.maintainers = with maintainers; [ govanify ]; +} diff --git a/nixos/modules/services/misc/nix-daemon.nix b/nixos/modules/services/misc/nix-daemon.nix index 133e96da0ec..70b27b7d3d0 100644 --- a/nixos/modules/services/misc/nix-daemon.nix +++ b/nixos/modules/services/misc/nix-daemon.nix @@ -458,7 +458,7 @@ in description = "The flake reference to which <option>from></option> is to be rewritten."; }; flake = mkOption { - type = types.unspecified; + type = types.nullOr types.attrs; default = null; example = literalExample "nixpkgs"; description = '' diff --git a/nixos/modules/services/monitoring/prometheus/exporters.nix b/nixos/modules/services/monitoring/prometheus/exporters.nix index d648de6a414..9182c2f2ed8 100644 --- a/nixos/modules/services/monitoring/prometheus/exporters.nix +++ b/nixos/modules/services/monitoring/prometheus/exporters.nix @@ -33,6 +33,7 @@ let "domain" "dovecot" "fritzbox" + "influxdb" "json" "jitsi" "kea" diff --git a/nixos/modules/services/monitoring/prometheus/exporters/influxdb.nix b/nixos/modules/services/monitoring/prometheus/exporters/influxdb.nix new file mode 100644 index 00000000000..ba45173e946 --- /dev/null +++ b/nixos/modules/services/monitoring/prometheus/exporters/influxdb.nix @@ -0,0 +1,34 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.influxdb; +in +{ + port = 9122; + extraOpts = { + sampleExpiry = mkOption { + type = types.str; + default = "5m"; + example = "10m"; + description = "How long a sample is valid for"; + }; + udpBindAddress = mkOption { + type = types.str; + default = ":9122"; + example = "192.0.2.1:9122"; + description = "Address on which to listen for udp packets"; + }; + }; + serviceOpts = { + serviceConfig = { + RuntimeDirectory = "prometheus-influxdb-exporter"; + ExecStart = '' + ${pkgs.prometheus-influxdb-exporter}/bin/influxdb_exporter \ + --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \ + --influxdb.sample-expiry ${cfg.sampleExpiry} ${concatStringsSep " " cfg.extraFlags} + ''; + }; + }; +} diff --git a/nixos/modules/services/network-filesystems/litestream/default.nix b/nixos/modules/services/network-filesystems/litestream/default.nix new file mode 100644 index 00000000000..f1806c5af0a --- /dev/null +++ b/nixos/modules/services/network-filesystems/litestream/default.nix @@ -0,0 +1,100 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.litestream; + settingsFormat = pkgs.formats.yaml {}; +in +{ + options.services.litestream = { + enable = mkEnableOption "litestream"; + + package = mkOption { + description = "Package to use."; + default = pkgs.litestream; + defaultText = "pkgs.litestream"; + type = types.package; + }; + + settings = mkOption { + description = '' + See the <link xlink:href="https://litestream.io/reference/config/">documentation</link>. + ''; + type = settingsFormat.type; + example = { + dbs = [ + { + path = "/var/lib/db1"; + replicas = [ + { + url = "s3://mybkt.litestream.io/db1"; + } + ]; + } + ]; + }; + }; + + environmentFile = mkOption { + type = types.nullOr types.path; + default = null; + example = "/run/secrets/litestream"; + description = '' + Environment file as defined in <citerefentry> + <refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum> + </citerefentry>. + + Secrets may be passed to the service without adding them to the + world-readable Nix store, by specifying placeholder variables as + the option value in Nix and setting these variables accordingly in the + environment file. + + By default, Litestream will perform environment variable expansion + within the config file before reading it. Any references to ''$VAR or + ''${VAR} formatted variables will be replaced with their environment + variable values. If no value is set then it will be replaced with an + empty string. + + <programlisting> + # Content of the environment file + LITESTREAM_ACCESS_KEY_ID=AKIAxxxxxxxxxxxxxxxx + LITESTREAM_SECRET_ACCESS_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/xxxxxxxxx + </programlisting> + + Note that this file needs to be available on the host on which + this exporter is running. + ''; + }; + }; + + config = mkIf cfg.enable { + environment.systemPackages = [ cfg.package ]; + environment.etc = { + "litestream.yml" = { + source = settingsFormat.generate "litestream-config.yaml" cfg.settings; + }; + }; + + systemd.services.litestream = { + description = "Litestream"; + wantedBy = [ "multi-user.target" ]; + after = [ "networking.target" ]; + serviceConfig = { + EnvironmentFile = mkIf (cfg.environmentFile != null) cfg.environmentFile; + ExecStart = "${cfg.package}/bin/litestream replicate"; + Restart = "always"; + User = "litestream"; + Group = "litestream"; + }; + }; + + users.users.litestream = { + description = "Litestream user"; + group = "litestream"; + isSystemUser = true; + }; + users.groups.litestream = {}; + }; + meta.doc = ./litestream.xml; +} diff --git a/nixos/modules/services/network-filesystems/litestream/litestream.xml b/nixos/modules/services/network-filesystems/litestream/litestream.xml new file mode 100644 index 00000000000..598f9be8cf6 --- /dev/null +++ b/nixos/modules/services/network-filesystems/litestream/litestream.xml @@ -0,0 +1,65 @@ +<chapter xmlns="http://docbook.org/ns/docbook" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:xi="http://www.w3.org/2001/XInclude" + version="5.0" + xml:id="module-services-litestream"> + <title>Litestream</title> + <para> + <link xlink:href="https://litestream.io/">Litestream</link> is a standalone streaming + replication tool for SQLite. + </para> + + <section xml:id="module-services-litestream-configuration"> + <title>Configuration</title> + + <para> + Litestream service is managed by a dedicated user named <literal>litestream</literal> + which needs permission to the database file. Here's an example config which gives + required permissions to access <link linkend="opt-services.grafana.database.path"> + grafana database</link>: +<programlisting> +{ pkgs, ... }: +{ + users.users.litestream.extraGroups = [ "grafana" ]; + + systemd.services.grafana.serviceConfig.ExecStartPost = "+" + pkgs.writeShellScript "grant-grafana-permissions" '' + timeout=10 + + while [ ! -f /var/lib/grafana/data/grafana.db ]; + do + if [ "$timeout" == 0 ]; then + echo "ERROR: Timeout while waiting for /var/lib/grafana/data/grafana.db." + exit 1 + fi + + sleep 1 + + ((timeout--)) + done + + find /var/lib/grafana -type d -exec chmod -v 775 {} \; + find /var/lib/grafana -type f -exec chmod -v 660 {} \; + ''; + + services.litestream = { + enable = true; + + environmentFile = "/run/secrets/litestream"; + + settings = { + dbs = [ + { + path = "/var/lib/grafana/data/grafana.db"; + replicas = [{ + url = "s3://mybkt.litestream.io/grafana"; + }]; + } + ]; + }; + }; +} +</programlisting> + </para> + </section> + +</chapter> diff --git a/nixos/modules/services/networking/bird.nix b/nixos/modules/services/networking/bird.nix index 1923afdf83f..c14adbda3c5 100644 --- a/nixos/modules/services/networking/bird.nix +++ b/nixos/modules/services/networking/bird.nix @@ -10,8 +10,8 @@ let birdBin = if variant == "bird6" then "bird6" else "bird"; birdc = if variant == "bird6" then "birdc6" else "birdc"; descr = - { bird = "1.9.x with IPv4 suport"; - bird6 = "1.9.x with IPv6 suport"; + { bird = "1.6.x with IPv4 support"; + bird6 = "1.6.x with IPv6 support"; bird2 = "2.x"; }.${variant}; in { diff --git a/nixos/modules/services/networking/firewall.nix b/nixos/modules/services/networking/firewall.nix index cdc3a172ea7..f982621e232 100644 --- a/nixos/modules/services/networking/firewall.nix +++ b/nixos/modules/services/networking/firewall.nix @@ -339,6 +339,8 @@ in description = '' Whether to log rejected or dropped incoming connections. + Note: The logs are found in the kernel logs, i.e. dmesg + or journalctl -k. ''; }; @@ -350,6 +352,8 @@ in Whether to log all rejected or dropped incoming packets. This tends to give a lot of log messages, so it's mostly useful for debugging. + Note: The logs are found in the kernel logs, i.e. dmesg + or journalctl -k. ''; }; diff --git a/nixos/modules/services/networking/kea.nix b/nixos/modules/services/networking/kea.nix index 72773b83a49..b11402204ae 100644 --- a/nixos/modules/services/networking/kea.nix +++ b/nixos/modules/services/networking/kea.nix @@ -238,6 +238,10 @@ in KEA_PIDFILE_DIR = "/run/kea"; }; + restartTriggers = [ + ctrlAgentConfig + ]; + serviceConfig = { ExecStart = "${package}/bin/kea-ctrl-agent -c /etc/kea/ctrl-agent.conf ${lib.escapeShellArgs cfg.dhcp4.extraArgs}"; KillMode = "process"; @@ -269,6 +273,10 @@ in KEA_PIDFILE_DIR = "/run/kea"; }; + restartTriggers = [ + dhcp4Config + ]; + serviceConfig = { ExecStart = "${package}/bin/kea-dhcp4 -c /etc/kea/dhcp4-server.conf ${lib.escapeShellArgs cfg.dhcp4.extraArgs}"; # Kea does not request capabilities by itself @@ -307,6 +315,10 @@ in KEA_PIDFILE_DIR = "/run/kea"; }; + restartTriggers = [ + dhcp6Config + ]; + serviceConfig = { ExecStart = "${package}/bin/kea-dhcp6 -c /etc/kea/dhcp6-server.conf ${lib.escapeShellArgs cfg.dhcp6.extraArgs}"; # Kea does not request capabilities by itself @@ -343,6 +355,10 @@ in KEA_PIDFILE_DIR = "/run/kea"; }; + restartTriggers = [ + dhcpDdnsConfig + ]; + serviceConfig = { ExecStart = "${package}/bin/kea-dhcp-ddns -c /etc/kea/dhcp-ddns.conf ${lib.escapeShellArgs cfg.dhcp-ddns.extraArgs}"; AmbientCapabilites = [ diff --git a/nixos/modules/services/networking/networkmanager.nix b/nixos/modules/services/networking/networkmanager.nix index 064018057cd..c8861171dd6 100644 --- a/nixos/modules/services/networking/networkmanager.nix +++ b/nixos/modules/services/networking/networkmanager.nix @@ -6,7 +6,6 @@ let cfg = config.networking.networkmanager; basePackages = with pkgs; [ - crda modemmanager networkmanager networkmanager-fortisslvpn @@ -49,6 +48,7 @@ let rc-manager = if config.networking.resolvconf.enable then "resolvconf" else "unmanaged"; + firewall-backend = cfg.firewallBackend; }) (mkSection "keyfile" { unmanaged-devices = @@ -244,6 +244,15 @@ in { ''; }; + firewallBackend = mkOption { + type = types.enum [ "iptables" "nftables" "none" ]; + default = "iptables"; + description = '' + Which firewall backend should be used for configuring masquerading with shared mode. + If set to none, NetworkManager doesn't manage the configuration at all. + ''; + }; + logLevel = mkOption { type = types.enum [ "OFF" "ERR" "WARN" "INFO" "DEBUG" "TRACE" ]; default = "WARN"; @@ -404,6 +413,8 @@ in { } ]; + hardware.wirelessRegulatoryDatabase = true; + environment.etc = with pkgs; { "NetworkManager/NetworkManager.conf".source = configFile; diff --git a/nixos/modules/services/networking/nftables.nix b/nixos/modules/services/networking/nftables.nix index cb75142965e..72f37c32253 100644 --- a/nixos/modules/services/networking/nftables.nix +++ b/nixos/modules/services/networking/nftables.nix @@ -103,6 +103,7 @@ in }]; boot.blacklistedKernelModules = [ "ip_tables" ]; environment.systemPackages = [ pkgs.nftables ]; + networking.networkmanager.firewallBackend = mkDefault "nftables"; systemd.services.nftables = { description = "nftables firewall"; before = [ "network-pre.target" ]; diff --git a/nixos/modules/services/networking/syncthing.nix b/nixos/modules/services/networking/syncthing.nix index 28348c7893a..2e92fe51e90 100644 --- a/nixos/modules/services/networking/syncthing.nix +++ b/nixos/modules/services/networking/syncthing.nix @@ -5,15 +5,16 @@ with lib; let cfg = config.services.syncthing; defaultUser = "syncthing"; + defaultGroup = defaultUser; devices = mapAttrsToList (name: device: { deviceID = device.id; inherit (device) name addresses introducer; - }) cfg.declarative.devices; + }) cfg.devices; folders = mapAttrsToList ( _: folder: { inherit (folder) path id label type; - devices = map (device: { deviceId = cfg.declarative.devices.${device}.id; }) folder.devices; + devices = map (device: { deviceId = cfg.devices.${device}.id; }) folder.devices; rescanIntervalS = folder.rescanInterval; fsWatcherEnabled = folder.watch; fsWatcherDelayS = folder.watchDelay; @@ -23,215 +24,218 @@ let }) (filterAttrs ( _: folder: folder.enable - ) cfg.declarative.folders); - - # get the api key by parsing the config.xml - getApiKey = pkgs.writers.writeDash "getAPIKey" '' - ${pkgs.libxml2}/bin/xmllint \ - --xpath 'string(configuration/gui/apikey)'\ - ${cfg.configDir}/config.xml - ''; + ) cfg.folders); updateConfig = pkgs.writers.writeDash "merge-syncthing-config" '' set -efu - # wait for syncthing port to open - until ${pkgs.curl}/bin/curl -Ss ${cfg.guiAddress} -o /dev/null; do - sleep 1 - done - - API_KEY=$(${getApiKey}) - OLD_CFG=$(${pkgs.curl}/bin/curl -Ss \ - -H "X-API-Key: $API_KEY" \ - ${cfg.guiAddress}/rest/system/config) - - # generate the new config by merging with the nixos config options - NEW_CFG=$(echo "$OLD_CFG" | ${pkgs.jq}/bin/jq -s '.[] as $in | $in * { - "devices": (${builtins.toJSON devices}${optionalString (! cfg.declarative.overrideDevices) " + $in.devices"}), - "folders": (${builtins.toJSON folders}${optionalString (! cfg.declarative.overrideFolders) " + $in.folders"}) - }') - - # POST the new config to syncthing - echo "$NEW_CFG" | ${pkgs.curl}/bin/curl -Ss \ - -H "X-API-Key: $API_KEY" \ - ${cfg.guiAddress}/rest/system/config -d @- - - # restart syncthing after sending the new config - ${pkgs.curl}/bin/curl -Ss \ - -H "X-API-Key: $API_KEY" \ - -X POST \ - ${cfg.guiAddress}/rest/system/restart + + # get the api key by parsing the config.xml + while + ! api_key=$(${pkgs.libxml2}/bin/xmllint \ + --xpath 'string(configuration/gui/apikey)' \ + ${cfg.configDir}/config.xml) + do sleep 1; done + + curl() { + ${pkgs.curl}/bin/curl -sS -H "X-API-Key: $api_key" \ + --retry 1000 --retry-delay 1 --retry-all-errors \ + "$@" + } + + # query the old config + old_cfg=$(curl ${cfg.guiAddress}/rest/config) + + # generate the new config by merging with the NixOS config options + new_cfg=$(echo "$old_cfg" | ${pkgs.jq}/bin/jq -c '. * { + "devices": (${builtins.toJSON devices}${optionalString (! cfg.overrideDevices) " + .devices"}), + "folders": (${builtins.toJSON folders}${optionalString (! cfg.overrideFolders) " + .folders"}) + } * ${builtins.toJSON cfg.extraOptions}') + + # send the new config + curl -X PUT -d "$new_cfg" ${cfg.guiAddress}/rest/config + + # restart Syncthing if required + if curl ${cfg.guiAddress}/rest/config/restart-required | + ${pkgs.jq}/bin/jq -e .requiresRestart > /dev/null; then + curl -X POST ${cfg.guiAddress}/rest/system/restart + fi ''; in { ###### interface options = { services.syncthing = { - enable = mkEnableOption '' - Syncthing - the self-hosted open-source alternative - to Dropbox and Bittorrent Sync. Initial interface will be - available on http://127.0.0.1:8384/. - ''; - - declarative = { - cert = mkOption { - type = types.nullOr types.str; - default = null; - description = '' - Path to users cert.pem file, will be copied into the syncthing's - <literal>configDir</literal> - ''; - }; + enable = mkEnableOption + "Syncthing, a self-hosted open-source alternative to Dropbox and Bittorrent Sync"; - key = mkOption { - type = types.nullOr types.str; - default = null; - description = '' - Path to users key.pem file, will be copied into the syncthing's - <literal>configDir</literal> - ''; - }; + cert = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + Path to the <literal>cert.pem</literal> file, which will be copied into Syncthing's + <link linkend="opt-services.syncthing.configDir">configDir</link>. + ''; + }; - overrideDevices = mkOption { - type = types.bool; - default = true; - description = '' - Whether to delete the devices which are not configured via the - <literal>declarative.devices</literal> option. - If set to false, devices added via the webinterface will - persist but will have to be deleted manually. - ''; + key = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + Path to the <literal>key.pem</literal> file, which will be copied into Syncthing's + <link linkend="opt-services.syncthing.configDir">configDir</link>. + ''; + }; + + overrideDevices = mkOption { + type = types.bool; + default = true; + description = '' + Whether to delete the devices which are not configured via the + <link linkend="opt-services.syncthing.devices">devices</link> option. + If set to <literal>false</literal>, devices added via the web + interface will persist and will have to be deleted manually. + ''; + }; + + devices = mkOption { + default = {}; + description = '' + Peers/devices which Syncthing should communicate with. + + Note that you can still add devices manually, but those changes + will be reverted on restart if <link linkend="opt-services.syncthing.overrideDevices">overrideDevices</link> + is enabled. + ''; + example = { + bigbox = { + id = "7CFNTQM-IMTJBHJ-3UWRDIU-ZGQJFR6-VCXZ3NB-XUH3KZO-N52ITXR-LAIYUAU"; + addresses = [ "tcp://192.168.0.10:51820" ]; + }; }; + type = types.attrsOf (types.submodule ({ name, ... }: { + options = { + + name = mkOption { + type = types.str; + default = name; + description = '' + The name of the device. + ''; + }; - devices = mkOption { - default = {}; - description = '' - Peers/devices which syncthing should communicate with. - ''; - example = { - bigbox = { - id = "7CFNTQM-IMTJBHJ-3UWRDIU-ZGQJFR6-VCXZ3NB-XUH3KZO-N52ITXR-LAIYUAU"; - addresses = [ "tcp://192.168.0.10:51820" ]; + addresses = mkOption { + type = types.listOf types.str; + default = []; + description = '' + The addresses used to connect to the device. + If this is left empty, dynamic configuration is attempted. + ''; + }; + + id = mkOption { + type = types.str; + description = '' + The device ID. See <link xlink:href="https://docs.syncthing.net/dev/device-ids.html"/>. + ''; }; + + introducer = mkOption { + type = types.bool; + default = false; + description = '' + Whether the device should act as an introducer and be allowed + to add folders on this computer. + See <link xlink:href="https://docs.syncthing.net/users/introducer.html"/>. + ''; + }; + }; - type = types.attrsOf (types.submodule ({ name, ... }: { - options = { - - name = mkOption { - type = types.str; - default = name; - description = '' - Name of the device - ''; - }; - - addresses = mkOption { - type = types.listOf types.str; - default = []; - description = '' - The addresses used to connect to the device. - If this is let empty, dynamic configuration is attempted - ''; - }; - - id = mkOption { - type = types.str; - description = '' - The id of the other peer, this is mandatory. It's documented at - https://docs.syncthing.net/dev/device-ids.html - ''; - }; - - introducer = mkOption { - type = types.bool; - default = false; - description = '' - If the device should act as an introducer and be allowed - to add folders on this computer. - ''; - }; + })); + }; + + overrideFolders = mkOption { + type = types.bool; + default = true; + description = '' + Whether to delete the folders which are not configured via the + <link linkend="opt-services.syncthing.folders">folders</link> option. + If set to <literal>false</literal>, folders added via the web + interface will persist and will have to be deleted manually. + ''; + }; + folders = mkOption { + default = {}; + description = '' + Folders which should be shared by Syncthing. + + Note that you can still add devices manually, but those changes + will be reverted on restart if <link linkend="opt-services.syncthing.overrideDevices">overrideDevices</link> + is enabled. + ''; + example = literalExample '' + { + "/home/user/sync" = { + id = "syncme"; + devices = [ "bigbox" ]; + }; + } + ''; + type = types.attrsOf (types.submodule ({ name, ... }: { + options = { + + enable = mkOption { + type = types.bool; + default = true; + description = '' + Whether to share this folder. + This option is useful when you want to define all folders + in one place, but not every machine should share all folders. + ''; }; - })); - }; - overrideFolders = mkOption { - type = types.bool; - default = true; - description = '' - Whether to delete the folders which are not configured via the - <literal>declarative.folders</literal> option. - If set to false, folders added via the webinterface will persist - but will have to be deleted manually. - ''; - }; + path = mkOption { + type = types.str; + default = name; + description = '' + The path to the folder which should be shared. + ''; + }; - folders = mkOption { - default = {}; - description = '' - folders which should be shared by syncthing. - ''; - example = literalExample '' - { - "/home/user/sync" = { - id = "syncme"; - devices = [ "bigbox" ]; - }; - } - ''; - type = types.attrsOf (types.submodule ({ name, ... }: { - options = { - - enable = mkOption { - type = types.bool; - default = true; - description = '' - share this folder. - This option is useful when you want to define all folders - in one place, but not every machine should share all folders. - ''; - }; - - path = mkOption { - type = types.str; - default = name; - description = '' - The path to the folder which should be shared. - ''; - }; - - id = mkOption { - type = types.str; - default = name; - description = '' - The id of the folder. Must be the same on all devices. - ''; - }; - - label = mkOption { - type = types.str; - default = name; - description = '' - The label of the folder. - ''; - }; - - devices = mkOption { - type = types.listOf types.str; - default = []; - description = '' - The devices this folder should be shared with. Must be defined - in the <literal>declarative.devices</literal> attribute. - ''; - }; - - versioning = mkOption { - default = null; - description = '' - How to keep changed/deleted files with syncthing. - There are 4 different types of versioning with different parameters. - See https://docs.syncthing.net/users/versioning.html - ''; - example = [ + id = mkOption { + type = types.str; + default = name; + description = '' + The ID of the folder. Must be the same on all devices. + ''; + }; + + label = mkOption { + type = types.str; + default = name; + description = '' + The label of the folder. + ''; + }; + + devices = mkOption { + type = types.listOf types.str; + default = []; + description = '' + The devices this folder should be shared with. Each device must + be defined in the <link linkend="opt-services.syncthing.devices">devices</link> option. + ''; + }; + + versioning = mkOption { + default = null; + description = '' + How to keep changed/deleted files with Syncthing. + There are 4 different types of versioning with different parameters. + See <link xlink:href="https://docs.syncthing.net/users/versioning.html"/>. + ''; + example = literalExample '' + [ { versioning = { type = "simple"; @@ -257,87 +261,99 @@ in { { versioning = { type = "external"; - params.versionsPath = pkgs.writers.writeBash "backup" '' + params.versionsPath = pkgs.writers.writeBash "backup" ''' folderpath="$1" filepath="$2" rm -rf "$folderpath/$filepath" - ''; + '''; }; } - ]; - type = with types; nullOr (submodule { - options = { - type = mkOption { - type = enum [ "external" "simple" "staggered" "trashcan" ]; - description = '' - Type of versioning. - See https://docs.syncthing.net/users/versioning.html - ''; - }; - params = mkOption { - type = attrsOf (either str path); - description = '' - Parameters for versioning. Structure depends on versioning.type. - See https://docs.syncthing.net/users/versioning.html - ''; - }; + ] + ''; + type = with types; nullOr (submodule { + options = { + type = mkOption { + type = enum [ "external" "simple" "staggered" "trashcan" ]; + description = '' + The type of versioning. + See <link xlink:href="https://docs.syncthing.net/users/versioning.html"/>. + ''; + }; + params = mkOption { + type = attrsOf (either str path); + description = '' + The parameters for versioning. Structure depends on + <link linkend="opt-services.syncthing.folders._name_.versioning.type">versioning.type</link>. + See <link xlink:href="https://docs.syncthing.net/users/versioning.html"/>. + ''; }; - }); - }; - - rescanInterval = mkOption { - type = types.int; - default = 3600; - description = '' - How often the folders should be rescaned for changes. - ''; - }; - - type = mkOption { - type = types.enum [ "sendreceive" "sendonly" "receiveonly" ]; - default = "sendreceive"; - description = '' - Whether to send only changes from this folder, only receive them - or propagate both. - ''; - }; - - watch = mkOption { - type = types.bool; - default = true; - description = '' - Whether the folder should be watched for changes by inotify. - ''; - }; - - watchDelay = mkOption { - type = types.int; - default = 10; - description = '' - The delay after an inotify event is triggered. - ''; - }; - - ignorePerms = mkOption { - type = types.bool; - default = true; - description = '' - Whether to propagate permission changes. - ''; - }; - - ignoreDelete = mkOption { - type = types.bool; - default = false; - description = '' - Whether to delete files in destination. See <link - xlink:href="https://docs.syncthing.net/advanced/folder-ignoredelete.html"> - upstream's docs</link>. - ''; - }; + }; + }); + }; + + rescanInterval = mkOption { + type = types.int; + default = 3600; + description = '' + How often the folder should be rescanned for changes. + ''; + }; + + type = mkOption { + type = types.enum [ "sendreceive" "sendonly" "receiveonly" ]; + default = "sendreceive"; + description = '' + Whether to only send changes for this folder, only receive them + or both. + ''; + }; + + watch = mkOption { + type = types.bool; + default = true; + description = '' + Whether the folder should be watched for changes by inotify. + ''; + }; + + watchDelay = mkOption { + type = types.int; + default = 10; + description = '' + The delay after an inotify event is triggered. + ''; + }; + + ignorePerms = mkOption { + type = types.bool; + default = true; + description = '' + Whether to ignore permission changes. + ''; + }; + ignoreDelete = mkOption { + type = types.bool; + default = false; + description = '' + Whether to skip deleting files that are deleted by peers. + See <link xlink:href="https://docs.syncthing.net/advanced/folder-ignoredelete.html"/>. + ''; }; - })); + }; + })); + }; + + extraOptions = mkOption { + type = types.addCheck (pkgs.formats.json {}).type isAttrs; + default = {}; + description = '' + Extra configuration options for Syncthing. + See <link xlink:href="https://docs.syncthing.net/users/config.html"/>. + ''; + example = { + options.localAnnounceEnabled = false; + gui.theme = "black"; }; }; @@ -345,31 +361,35 @@ in { type = types.str; default = "127.0.0.1:8384"; description = '' - Address to serve the GUI. + The address to serve the web interface at. ''; }; systemService = mkOption { type = types.bool; default = true; - description = "Auto launch Syncthing as a system service."; + description = '' + Whether to auto-launch Syncthing as a system service. + ''; }; user = mkOption { type = types.str; default = defaultUser; + example = "yourUser"; description = '' - Syncthing will be run under this user (user will be created if it doesn't exist. - This can be your user name). + The user to run Syncthing as. + By default, a user named <literal>${defaultUser}</literal> will be created. ''; }; group = mkOption { type = types.str; - default = defaultUser; + default = defaultGroup; + example = "yourGroup"; description = '' - Syncthing will be run under this group (group will not be created if it doesn't exist. - This can be your user name). + The group to run Syncthing under. + By default, a group named <literal>${defaultGroup}</literal> will be created. ''; }; @@ -378,63 +398,67 @@ in { default = null; example = "socks5://address.com:1234"; description = '' - Overwrites all_proxy environment variable for the syncthing process to - the given value. This is normaly used to let relay client connect - through SOCKS5 proxy server. + Overwrites the all_proxy environment variable for the Syncthing process to + the given value. This is normally used to let Syncthing connect + through a SOCKS5 proxy server. + See <link xlink:href="https://docs.syncthing.net/users/proxying.html"/>. ''; }; dataDir = mkOption { type = types.path; default = "/var/lib/syncthing"; + example = "/home/yourUser"; description = '' - Path where synced directories will exist. + The path where synchronised directories will exist. ''; }; - configDir = mkOption { + configDir = let + cond = versionAtLeast config.system.stateVersion "19.03"; + in mkOption { type = types.path; description = '' - Path where the settings and keys will exist. + The path where the settings and keys will exist. ''; - default = - let - nixos = config.system.stateVersion; - cond = versionAtLeast nixos "19.03"; - in cfg.dataDir + (optionalString cond "/.config/syncthing"); + default = cfg.dataDir + (optionalString cond "/.config/syncthing"); + defaultText = literalExample "dataDir${optionalString cond " + \"/.config/syncthing\""}"; }; openDefaultPorts = mkOption { type = types.bool; default = false; - example = literalExample "true"; + example = true; description = '' - Open the default ports in the firewall: - - TCP 22000 for transfers - - UDP 21027 for discovery - If multiple users are running syncthing on this machine, you will need to manually open a set of ports for each instance and leave this disabled. - Alternatively, if are running only a single instance on this machine using the default ports, enable this. + Whether to open the default ports in the firewall: TCP 22000 for transfers + and UDP 21027 for discovery. + + If multiple users are running Syncthing on this machine, you will need + to manually open a set of ports for each instance and leave this disabled. + Alternatively, if you are running only a single instance on this machine + using the default ports, enable this. ''; }; package = mkOption { type = types.package; default = pkgs.syncthing; - defaultText = "pkgs.syncthing"; - example = literalExample "pkgs.syncthing"; + defaultText = literalExample "pkgs.syncthing"; description = '' - Syncthing package to use. + The Syncthing package to use. ''; }; }; }; imports = [ - (mkRemovedOptionModule ["services" "syncthing" "useInotify"] '' - This option was removed because syncthing now has the inotify functionality included under the name "fswatcher". - It can be enabled on a per-folder basis through the webinterface. + (mkRemovedOptionModule [ "services" "syncthing" "useInotify" ] '' + This option was removed because Syncthing now has the inotify functionality included under the name "fswatcher". + It can be enabled on a per-folder basis through the web interface. '') - ]; + ] ++ map (o: + mkRenamedOptionModule [ "services" "syncthing" "declarative" o ] [ "services" "syncthing" o ] + ) [ "cert" "key" "devices" "folders" "overrideDevices" "overrideFolders" "extraOptions"]; ###### implementation @@ -457,8 +481,8 @@ in { }; }; - users.groups = mkIf (cfg.systemService && cfg.group == defaultUser) { - ${defaultUser}.gid = + users.groups = mkIf (cfg.systemService && cfg.group == defaultGroup) { + ${defaultGroup}.gid = config.ids.gids.syncthing; }; @@ -478,14 +502,14 @@ in { RestartForceExitStatus="3 4"; User = cfg.user; Group = cfg.group; - ExecStartPre = mkIf (cfg.declarative.cert != null || cfg.declarative.key != null) + ExecStartPre = mkIf (cfg.cert != null || cfg.key != null) "+${pkgs.writers.writeBash "syncthing-copy-keys" '' install -dm700 -o ${cfg.user} -g ${cfg.group} ${cfg.configDir} - ${optionalString (cfg.declarative.cert != null) '' - install -Dm400 -o ${cfg.user} -g ${cfg.group} ${toString cfg.declarative.cert} ${cfg.configDir}/cert.pem + ${optionalString (cfg.cert != null) '' + install -Dm400 -o ${cfg.user} -g ${cfg.group} ${toString cfg.cert} ${cfg.configDir}/cert.pem ''} - ${optionalString (cfg.declarative.key != null) '' - install -Dm400 -o ${cfg.user} -g ${cfg.group} ${toString cfg.declarative.key} ${cfg.configDir}/key.pem + ${optionalString (cfg.key != null) '' + install -Dm400 -o ${cfg.user} -g ${cfg.group} ${toString cfg.key} ${cfg.configDir}/key.pem ''} ''}" ; @@ -516,8 +540,10 @@ in { }; }; syncthing-init = mkIf ( - cfg.declarative.devices != {} || cfg.declarative.folders != {} + cfg.devices != {} || cfg.folders != {} || cfg.extraOptions != {} ) { + description = "Syncthing configuration updater"; + requisite = [ "syncthing.service" ]; after = [ "syncthing.service" ]; wantedBy = [ "multi-user.target" ]; diff --git a/nixos/modules/services/networking/wpa_supplicant.nix b/nixos/modules/services/networking/wpa_supplicant.nix index c0a4ce40760..494d21cc867 100644 --- a/nixos/modules/services/networking/wpa_supplicant.nix +++ b/nixos/modules/services/networking/wpa_supplicant.nix @@ -241,7 +241,8 @@ in { environment.systemPackages = [ package ]; services.dbus.packages = [ package ]; - services.udev.packages = [ pkgs.crda ]; + + hardware.wirelessRegulatoryDatabase = true; # FIXME: start a separate wpa_supplicant instance per interface. systemd.services.wpa_supplicant = let diff --git a/nixos/modules/services/system/uptimed.nix b/nixos/modules/services/system/uptimed.nix index 1e256c51408..67a03876e19 100644 --- a/nixos/modules/services/system/uptimed.nix +++ b/nixos/modules/services/system/uptimed.nix @@ -4,7 +4,7 @@ with lib; let cfg = config.services.uptimed; - stateDir = "/var/spool/uptimed"; + stateDir = "/var/lib/uptimed"; in { options = { @@ -21,12 +21,16 @@ in }; config = mkIf cfg.enable { + + environment.systemPackages = [ pkgs.uptimed ]; + users.users.uptimed = { description = "Uptimed daemon user"; home = stateDir; - createHome = true; uid = config.ids.uids.uptimed; + group = "uptimed"; }; + users.groups.uptimed = {}; systemd.services.uptimed = { unitConfig.Documentation = "man:uptimed(8) man:uprecords(1)"; @@ -41,7 +45,7 @@ in PrivateTmp = "yes"; PrivateNetwork = "yes"; NoNewPrivileges = "yes"; - ReadWriteDirectories = stateDir; + StateDirectory = [ "uptimed" ]; InaccessibleDirectories = "/home"; ExecStart = "${pkgs.uptimed}/sbin/uptimed -f -p ${stateDir}/pid"; }; diff --git a/nixos/modules/services/web-apps/node-red.nix b/nixos/modules/services/web-apps/node-red.nix new file mode 100644 index 00000000000..16cfb29cf57 --- /dev/null +++ b/nixos/modules/services/web-apps/node-red.nix @@ -0,0 +1,148 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.node-red; + defaultUser = "node-red"; + finalPackage = if cfg.withNpmAndGcc then node-red_withNpmAndGcc else cfg.package; + node-red_withNpmAndGcc = pkgs.runCommandNoCC "node-red" { + nativeBuildInputs = [ pkgs.makeWrapper ]; + } + '' + mkdir -p $out/bin + makeWrapper ${pkgs.nodePackages.node-red}/bin/node-red $out/bin/node-red \ + --set PATH '${lib.makeBinPath [ pkgs.nodePackages.npm pkgs.gcc ]}:$PATH' \ + ''; +in +{ + options.services.node-red = { + enable = mkEnableOption "the Node-RED service"; + + package = mkOption { + default = pkgs.nodePackages.node-red; + defaultText = "pkgs.nodePackages.node-red"; + type = types.package; + description = "Node-RED package to use."; + }; + + openFirewall = mkOption { + type = types.bool; + default = false; + description = '' + Open ports in the firewall for the server. + ''; + }; + + withNpmAndGcc = mkOption { + type = types.bool; + default = false; + description = '' + Give Node-RED access to NPM and GCC at runtime, so 'Nodes' can be + downloaded and managed imperatively via the 'Palette Manager'. + ''; + }; + + configFile = mkOption { + type = types.path; + default = "${cfg.package}/lib/node_modules/node-red/settings.js"; + defaultText = "\${cfg.package}/lib/node_modules/node-red/settings.js"; + description = '' + Path to the JavaScript configuration file. + See <link + xlink:href="https://github.com/node-red/node-red/blob/master/packages/node_modules/node-red/settings.js"/> + for a configuration example. + ''; + }; + + port = mkOption { + type = types.port; + default = 1880; + description = "Listening port."; + }; + + user = mkOption { + type = types.str; + default = defaultUser; + description = '' + User under which Node-RED runs.If left as the default value this user + will automatically be created on system activation, otherwise the + sysadmin is responsible for ensuring the user exists. + ''; + }; + + group = mkOption { + type = types.str; + default = defaultUser; + description = '' + Group under which Node-RED runs.If left as the default value this group + will automatically be created on system activation, otherwise the + sysadmin is responsible for ensuring the group exists. + ''; + }; + + userDir = mkOption { + type = types.path; + default = "/var/lib/node-red"; + description = '' + The directory to store all user data, such as flow and credential files and all library data. If left + as the default value this directory will automatically be created before the node-red service starts, + otherwise the sysadmin is responsible for ensuring the directory exists with appropriate ownership + and permissions. + ''; + }; + + safe = mkOption { + type = types.bool; + default = false; + description = "Whether to launch Node-RED in --safe mode."; + }; + + define = mkOption { + type = types.attrs; + default = {}; + description = "List of settings.js overrides to pass via -D to Node-RED."; + example = literalExample '' + { + "logging.console.level" = "trace"; + } + ''; + }; + }; + + config = mkIf cfg.enable { + users.users = optionalAttrs (cfg.user == defaultUser) { + ${defaultUser} = { + isSystemUser = true; + }; + }; + + users.groups = optionalAttrs (cfg.group == defaultUser) { + ${defaultUser} = { }; + }; + + networking.firewall = mkIf cfg.openFirewall { + allowedTCPPorts = [ cfg.port ]; + }; + + systemd.services.node-red = { + description = "Node-RED Service"; + wantedBy = [ "multi-user.target" ]; + after = [ "networking.target" ]; + environment = { + HOME = cfg.userDir; + }; + serviceConfig = mkMerge [ + { + User = cfg.user; + Group = cfg.group; + ExecStart = "${finalPackage}/bin/node-red ${pkgs.lib.optionalString cfg.safe "--safe"} --settings ${cfg.configFile} --port ${toString cfg.port} --userDir ${cfg.userDir} ${concatStringsSep " " (mapAttrsToList (name: value: "-D ${name}=${value}") cfg.define)}"; + PrivateTmp = true; + Restart = "always"; + WorkingDirectory = cfg.userDir; + } + (mkIf (cfg.userDir == "/var/lib/node-red") { StateDirectory = "node-red"; }) + ]; + }; + }; +} diff --git a/nixos/modules/services/web-apps/tt-rss.nix b/nixos/modules/services/web-apps/tt-rss.nix index b78487cc928..ed13845915c 100644 --- a/nixos/modules/services/web-apps/tt-rss.nix +++ b/nixos/modules/services/web-apps/tt-rss.nix @@ -19,82 +19,84 @@ let mysqlLocal = cfg.database.createLocally && cfg.database.type == "mysql"; pgsqlLocal = cfg.database.createLocally && cfg.database.type == "pgsql"; - tt-rss-config = pkgs.writeText "config.php" '' + tt-rss-config = let + password = + if (cfg.database.password != null) then + "${(escape ["'" "\\"] cfg.database.password)}" + else if (cfg.database.passwordFile != null) then + "file_get_contents('${cfg.database.passwordFile}'" + else + "" + ; + in pkgs.writeText "config.php" '' <?php + putenv('TTRSS_PHP_EXECUTABLE=${pkgs.php}/bin/php'); - define('PHP_EXECUTABLE', '${pkgs.php}/bin/php'); + putenv('TTRSS_LOCK_DIRECTORY=${lockDir}'); + putenv('TTRSS_CACHE_DIR=${cacheDir}'); + putenv('TTRSS_ICONS_DIR=${feedIconsDir}'); + putenv('TTRSS_ICONS_URL=${feedIconsDir}'); + putenv('TTRSS_SELF_URL_PATH=${cfg.selfUrlPath}'); - define('LOCK_DIRECTORY', '${lockDir}'); - define('CACHE_DIR', '${cacheDir}'); - define('ICONS_DIR', '${feedIconsDir}'); - define('ICONS_URL', '${feedIconsDir}'); - define('SELF_URL_PATH', '${cfg.selfUrlPath}'); + putenv('TTRSS_MYSQL_CHARSET=UTF8'); - define('MYSQL_CHARSET', 'UTF8'); + putenv('TTRSS_DB_TYPE=${cfg.database.type}'); + putenv('TTRSS_DB_HOST=${optionalString (cfg.database.host != null) cfg.database.host}'); + putenv('TTRSS_DB_USER=${cfg.database.user}'); + putenv('TTRSS_DB_NAME=${cfg.database.name}'); + putenv('TTRSS_DB_PASS=${password}'); + putenv('TTRSS_DB_PORT=${toString dbPort}'); - define('DB_TYPE', '${cfg.database.type}'); - define('DB_HOST', '${optionalString (cfg.database.host != null) cfg.database.host}'); - define('DB_USER', '${cfg.database.user}'); - define('DB_NAME', '${cfg.database.name}'); - define('DB_PASS', ${ - if (cfg.database.password != null) then - "'${(escape ["'" "\\"] cfg.database.password)}'" - else if (cfg.database.passwordFile != null) then - "file_get_contents('${cfg.database.passwordFile}')" - else - "''" - }); - define('DB_PORT', '${toString dbPort}'); + putenv('TTRSS_AUTH_AUTO_CREATE=${boolToString cfg.auth.autoCreate}'); + putenv('TTRSS_AUTH_AUTO_LOGIN=${boolToString cfg.auth.autoLogin}'); - define('AUTH_AUTO_CREATE', ${boolToString cfg.auth.autoCreate}); - define('AUTH_AUTO_LOGIN', ${boolToString cfg.auth.autoLogin}); + putenv('TTRSS_FEED_CRYPT_KEY=${escape ["'" "\\"] cfg.feedCryptKey}'); - define('FEED_CRYPT_KEY', '${escape ["'" "\\"] cfg.feedCryptKey}'); + putenv('TTRSS_SINGLE_USER_MODE=${boolToString cfg.singleUserMode}'); - define('SINGLE_USER_MODE', ${boolToString cfg.singleUserMode}); + putenv('TTRSS_SIMPLE_UPDATE_MODE=${boolToString cfg.simpleUpdateMode}'); - define('SIMPLE_UPDATE_MODE', ${boolToString cfg.simpleUpdateMode}); + # Never check for updates - the running version of the code should + # be controlled entirely by the version of TT-RSS active in the + # current Nix profile. If TT-RSS updates itself to a version + # requiring a database schema upgrade, and then the SystemD + # tt-rss.service is restarted, the old code copied from the Nix + # store will overwrite the updated version, causing the code to + # detect the need for a schema "upgrade" (since the schema version + # in the database is different than in the code), but the update + # schema operation in TT-RSS will do nothing because the schema + # version in the database is newer than that in the code. + putenv('TTRSS_CHECK_FOR_UPDATES=false'); - // Never check for updates - the running version of the code should be - // controlled entirely by the version of TT-RSS active in the current Nix - // profile. If TT-RSS updates itself to a version requiring a database - // schema upgrade, and then the SystemD tt-rss.service is restarted, the - // old code copied from the Nix store will overwrite the updated version, - // causing the code to detect the need for a schema "upgrade" (since the - // schema version in the database is different than in the code), but the - // update schema operation in TT-RSS will do nothing because the schema - // version in the database is newer than that in the code. - define('CHECK_FOR_UPDATES', false); + putenv('TTRSS_FORCE_ARTICLE_PURGE=${toString cfg.forceArticlePurge}'); + putenv('TTRSS_SESSION_COOKIE_LIFETIME=${toString cfg.sessionCookieLifetime}'); + putenv('TTRSS_ENABLE_GZIP_OUTPUT=${boolToString cfg.enableGZipOutput}'); - define('FORCE_ARTICLE_PURGE', ${toString cfg.forceArticlePurge}); - define('SESSION_COOKIE_LIFETIME', ${toString cfg.sessionCookieLifetime}); - define('ENABLE_GZIP_OUTPUT', ${boolToString cfg.enableGZipOutput}); + putenv('TTRSS_PLUGINS=${builtins.concatStringsSep "," cfg.plugins}'); - define('PLUGINS', '${builtins.concatStringsSep "," cfg.plugins}'); + putenv('TTRSS_LOG_DESTINATION=${cfg.logDestination}'); + putenv('TTRSS_CONFIG_VERSION=${toString configVersion}'); - define('LOG_DESTINATION', '${cfg.logDestination}'); - define('CONFIG_VERSION', ${toString configVersion}); + putenv('TTRSS_PUBSUBHUBBUB_ENABLED=${boolToString cfg.pubSubHubbub.enable}'); + putenv('TTRSS_PUBSUBHUBBUB_HUB=${cfg.pubSubHubbub.hub}'); - define('PUBSUBHUBBUB_ENABLED', ${boolToString cfg.pubSubHubbub.enable}); - define('PUBSUBHUBBUB_HUB', '${cfg.pubSubHubbub.hub}'); + putenv('TTRSS_SPHINX_SERVER=${cfg.sphinx.server}'); + putenv('TTRSS_SPHINX_INDEX=${builtins.concatStringsSep "," cfg.sphinx.index}'); - define('SPHINX_SERVER', '${cfg.sphinx.server}'); - define('SPHINX_INDEX', '${builtins.concatStringsSep "," cfg.sphinx.index}'); + putenv('TTRSS_ENABLE_REGISTRATION=${boolToString cfg.registration.enable}'); + putenv('TTRSS_REG_NOTIFY_ADDRESS=${cfg.registration.notifyAddress}'); + putenv('TTRSS_REG_MAX_USERS=${toString cfg.registration.maxUsers}'); - define('ENABLE_REGISTRATION', ${boolToString cfg.registration.enable}); - define('REG_NOTIFY_ADDRESS', '${cfg.registration.notifyAddress}'); - define('REG_MAX_USERS', ${toString cfg.registration.maxUsers}); + putenv('TTRSS_SMTP_SERVER=${cfg.email.server}'); + putenv('TTRSS_SMTP_LOGIN=${cfg.email.login}'); + putenv('TTRSS_SMTP_PASSWORD=${escape ["'" "\\"] cfg.email.password}'); + putenv('TTRSS_SMTP_SECURE=${cfg.email.security}'); - define('SMTP_SERVER', '${cfg.email.server}'); - define('SMTP_LOGIN', '${cfg.email.login}'); - define('SMTP_PASSWORD', '${escape ["'" "\\"] cfg.email.password}'); - define('SMTP_SECURE', '${cfg.email.security}'); - - define('SMTP_FROM_NAME', '${escape ["'" "\\"] cfg.email.fromName}'); - define('SMTP_FROM_ADDRESS', '${escape ["'" "\\"] cfg.email.fromAddress}'); - define('DIGEST_SUBJECT', '${escape ["'" "\\"] cfg.email.digestSubject}'); + putenv('TTRSS_SMTP_FROM_NAME=${escape ["'" "\\"] cfg.email.fromName}'); + putenv('TTRSS_SMTP_FROM_ADDRESS=${escape ["'" "\\"] cfg.email.fromAddress}'); + putenv('TTRSS_DIGEST_SUBJECT=${escape ["'" "\\"] cfg.email.digestSubject}'); ${cfg.extraConfig} ''; @@ -564,9 +566,12 @@ let "Z '${cfg.root}' 0755 ${cfg.user} tt_rss - -" ]; - systemd.services.tt-rss = - { + systemd.services = { + phpfpm-tt-rss = mkIf (cfg.pool == "${poolName}") { + restartTriggers = [ tt-rss-config pkgs.tt-rss ]; + }; + tt-rss = { description = "Tiny Tiny RSS feeds update daemon"; preStart = let @@ -604,6 +609,9 @@ let ''} ln -sf "${tt-rss-config}" "${cfg.root}/config.php" chmod -R 755 "${cfg.root}" + chmod -R ug+rwX "${cfg.root}/${lockDir}" + chmod -R ug+rwX "${cfg.root}/${cacheDir}" + chmod -R ug+rwX "${cfg.root}/${feedIconsDir}" '' + (optionalString (cfg.database.type == "pgsql") '' @@ -640,6 +648,7 @@ let wantedBy = [ "multi-user.target" ]; requires = optional mysqlLocal "mysql.service" ++ optional pgsqlLocal "postgresql.service"; after = [ "network.target" ] ++ optional mysqlLocal "mysql.service" ++ optional pgsqlLocal "postgresql.service"; + }; }; services.mysql = mkIf mysqlLocal { diff --git a/nixos/modules/services/x11/display-managers/set-session.py b/nixos/modules/services/x11/display-managers/set-session.py index 0cca80af44e..75940efe32b 100755 --- a/nixos/modules/services/x11/display-managers/set-session.py +++ b/nixos/modules/services/x11/display-managers/set-session.py @@ -72,11 +72,14 @@ def main(): f"Setting session name: {session}, as we found the existing wayland-session: {session_file}" ) user.set_session(session) + user.set_session_type("wayland") elif is_session_xsession(session_file): logging.debug( f"Setting session name: {session}, as we found the existing xsession: {session_file}" ) user.set_x_session(session) + user.set_session(session) + user.set_session_type("x11") else: logging.error(f"Couldn't figure out session type for {session_file}") sys.exit(1) diff --git a/nixos/modules/system/boot/systemd.nix b/nixos/modules/system/boot/systemd.nix index 58064e5de86..73616065a08 100644 --- a/nixos/modules/system/boot/systemd.nix +++ b/nixos/modules/system/boot/systemd.nix @@ -1044,7 +1044,7 @@ in done '' + concatMapStrings (name: optionalString (hasPrefix "tmpfiles.d/" name) '' rm -f $out/${removePrefix "tmpfiles.d/" name} - '') config.system.build.etc.targets; + '') config.system.build.etc.passthru.targets; }) + "/*"; "systemd/system-generators" = { source = hooks "generators" cfg.generators; }; @@ -1053,6 +1053,7 @@ in services.dbus.enable = true; + users.users.systemd-coredump.uid = config.ids.uids.systemd-coredump; users.users.systemd-network.uid = config.ids.uids.systemd-network; users.groups.systemd-network.gid = config.ids.gids.systemd-network; users.users.systemd-resolve.uid = config.ids.uids.systemd-resolve; diff --git a/nixos/modules/system/etc/etc.nix b/nixos/modules/system/etc/etc.nix index a450f303572..183de729210 100644 --- a/nixos/modules/system/etc/etc.nix +++ b/nixos/modules/system/etc/etc.nix @@ -8,21 +8,61 @@ let etc' = filter (f: f.enable) (attrValues config.environment.etc); - etc = pkgs.stdenvNoCC.mkDerivation { - name = "etc"; - - builder = ./make-etc.sh; - + etc = pkgs.runCommand "etc" { preferLocalBuild = true; allowSubstitutes = false; - /* !!! Use toXML. */ - sources = map (x: x.source) etc'; - targets = map (x: x.target) etc'; - modes = map (x: x.mode) etc'; - users = map (x: x.user) etc'; - groups = map (x: x.group) etc'; - }; + # This is needed for the systemd module + passthru.targets = map (x: x.target) etc'; + } /* sh */ '' + set -euo pipefail + + makeEtcEntry() { + src="$1" + target="$2" + mode="$3" + user="$4" + group="$5" + + if [[ "$src" = *'*'* ]]; then + # If the source name contains '*', perform globbing. + mkdir -p "$out/etc/$target" + for fn in $src; do + ln -s "$fn" "$out/etc/$target/" + done + else + + mkdir -p "$out/etc/$(dirname "$target")" + if ! [ -e "$out/etc/$target" ]; then + ln -s "$src" "$out/etc/$target" + else + echo "duplicate entry $target -> $src" + if [ "$(readlink "$out/etc/$target")" != "$src" ]; then + echo "mismatched duplicate entry $(readlink "$out/etc/$target") <-> $src" + ret=1 + + continue + fi + fi + + if [ "$mode" != symlink ]; then + echo "$mode" > "$out/etc/$target.mode" + echo "$user" > "$out/etc/$target.uid" + echo "$group" > "$out/etc/$target.gid" + fi + fi + } + + mkdir -p "$out/etc" + ${concatMapStringsSep "\n" (etcEntry: escapeShellArgs [ + "makeEtcEntry" + etcEntry.source + etcEntry.target + etcEntry.mode + etcEntry.user + etcEntry.group + ]) etc'} + ''; in diff --git a/nixos/modules/system/etc/make-etc.sh b/nixos/modules/system/etc/make-etc.sh deleted file mode 100644 index aabfb5e88a6..00000000000 --- a/nixos/modules/system/etc/make-etc.sh +++ /dev/null @@ -1,45 +0,0 @@ -source $stdenv/setup - -mkdir -p $out/etc - -set -f -sources_=($sources) -targets_=($targets) -modes_=($modes) -users_=($users) -groups_=($groups) -set +f - -for ((i = 0; i < ${#targets_[@]}; i++)); do - source="${sources_[$i]}" - target="${targets_[$i]}" - - if [[ "$source" =~ '*' ]]; then - - # If the source name contains '*', perform globbing. - mkdir -p $out/etc/$target - for fn in $source; do - ln -s "$fn" $out/etc/$target/ - done - - else - - mkdir -p $out/etc/$(dirname $target) - if ! [ -e $out/etc/$target ]; then - ln -s $source $out/etc/$target - else - echo "duplicate entry $target -> $source" - if test "$(readlink $out/etc/$target)" != "$source"; then - echo "mismatched duplicate entry $(readlink $out/etc/$target) <-> $source" - exit 1 - fi - fi - - if test "${modes_[$i]}" != symlink; then - echo "${modes_[$i]}" > $out/etc/$target.mode - echo "${users_[$i]}" > $out/etc/$target.uid - echo "${groups_[$i]}" > $out/etc/$target.gid - fi - - fi -done diff --git a/nixos/modules/virtualisation/virtualbox-image.nix b/nixos/modules/virtualisation/virtualbox-image.nix index 272c696807a..1a0c4df42cb 100644 --- a/nixos/modules/virtualisation/virtualbox-image.nix +++ b/nixos/modules/virtualisation/virtualbox-image.nix @@ -18,6 +18,13 @@ in { The size of the VirtualBox base image in MiB. ''; }; + baseImageFreeSpace = mkOption { + type = with types; int; + default = 30 * 1024; + description = '' + Free space in the VirtualBox base image in MiB. + ''; + }; memorySize = mkOption { type = types.int; default = 1536; @@ -129,6 +136,7 @@ in { inherit pkgs lib config; partitionTableType = "legacy"; diskSize = cfg.baseImageSize; + additionalSpace = "${toString cfg.baseImageFreeSpace}M"; postVM = '' diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index d6ef7d42431..ce91d651888 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -204,6 +204,7 @@ in k3s = handleTest ./k3s.nix {}; kafka = handleTest ./kafka.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 {}; keepalived = handleTest ./keepalived.nix {}; keepassxc = handleTest ./keepassxc.nix {}; @@ -222,10 +223,12 @@ in latestKernel.hardened = handleTest ./hardened.nix { latestKernel = true; }; latestKernel.login = handleTest ./login.nix { latestKernel = true; }; leaps = handleTest ./leaps.nix {}; + libreddit = handleTest ./libreddit.nix {}; lidarr = handleTest ./lidarr.nix {}; libreswan = handleTest ./libreswan.nix {}; lightdm = handleTest ./lightdm.nix {}; limesurvey = handleTest ./limesurvey.nix {}; + litestream = handleTest ./litestream.nix {}; locate = handleTest ./locate.nix {}; login = handleTest ./login.nix {}; loki = handleTest ./loki.nix {}; @@ -258,6 +261,7 @@ in morty = handleTest ./morty.nix {}; mosquitto = handleTest ./mosquitto.nix {}; mpd = handleTest ./mpd.nix {}; + mpv = handleTest ./mpv.nix {}; mumble = handleTest ./mumble.nix {}; musescore = handleTest ./musescore.nix {}; munin = handleTest ./munin.nix {}; @@ -300,6 +304,7 @@ in nix-serve = handleTest ./nix-ssh-serve.nix {}; nix-ssh-serve = handleTest ./nix-ssh-serve.nix {}; nixos-generate-config = handleTest ./nixos-generate-config.nix {}; + node-red = handleTest ./node-red.nix {}; nomad = handleTest ./nomad.nix {}; novacomd = handleTestOn ["x86_64-linux"] ./novacomd.nix {}; nsd = handleTest ./nsd.nix {}; diff --git a/nixos/tests/gitlab.nix b/nixos/tests/gitlab.nix index 696ebabb580..fc153bb06ec 100644 --- a/nixos/tests/gitlab.nix +++ b/nixos/tests/gitlab.nix @@ -51,10 +51,11 @@ import ./make-test-python.nix ({ pkgs, lib, ...} : with lib; { host = "localhost"; port = 143; }; - pages = { - enabled = true; - host = "localhost"; - }; + # https://github.com/NixOS/nixpkgs/issues/132295 + # pages = { + # enabled = true; + # host = "localhost"; + # }; }; secrets = { secretFile = pkgs.writeText "secret" "Aig5zaic"; @@ -90,7 +91,8 @@ import ./make-test-python.nix ({ pkgs, lib, ...} : with lib; { waitForServices = '' gitlab.wait_for_unit("gitaly.service") gitlab.wait_for_unit("gitlab-workhorse.service") - gitlab.wait_for_unit("gitlab-pages.service") + # https://github.com/NixOS/nixpkgs/issues/132295 + # gitlab.wait_for_unit("gitlab-pages.service") gitlab.wait_for_unit("gitlab-mailroom.service") gitlab.wait_for_unit("gitlab.service") gitlab.wait_for_unit("gitlab-sidekiq.service") diff --git a/nixos/tests/kbd-update-search-paths-patch.nix b/nixos/tests/kbd-update-search-paths-patch.nix new file mode 100644 index 00000000000..2967ee48903 --- /dev/null +++ b/nixos/tests/kbd-update-search-paths-patch.nix @@ -0,0 +1,18 @@ +import ./make-test-python.nix ({ pkgs, ... }: { + name = "kbd-update-search-paths-patch"; + + machine = { pkgs, options, ... }: { + console = { + packages = options.console.packages.default ++ [ pkgs.terminus_font ]; + }; + }; + + testScript = '' + command = "${pkgs.kbd}/bin/setfont ter-112n 2>&1" + (status, out) = machine.execute(command) + pattern = re.compile(r".*Unable to find file:.*") + match = pattern.match(out) + if match: + raise Exception("command `{}` failed".format(command)) + ''; +}) diff --git a/nixos/tests/libreddit.nix b/nixos/tests/libreddit.nix new file mode 100644 index 00000000000..f7ef701d086 --- /dev/null +++ b/nixos/tests/libreddit.nix @@ -0,0 +1,19 @@ +import ./make-test-python.nix ({ lib, ... }: + +with lib; + +{ + name = "libreddit"; + meta.maintainers = with maintainers; [ fab ]; + + nodes.machine = + { pkgs, ... }: + { services.libreddit.enable = true; }; + + testScript = '' + machine.wait_for_unit("libreddit.service") + machine.wait_for_open_port("8080") + # The service wants to get data from https://www.reddit.com + machine.succeed("curl http://localhost:8080/") + ''; +}) diff --git a/nixos/tests/litestream.nix b/nixos/tests/litestream.nix new file mode 100644 index 00000000000..886fbfef9cf --- /dev/null +++ b/nixos/tests/litestream.nix @@ -0,0 +1,93 @@ +import ./make-test-python.nix ({ pkgs, ...} : { + name = "litestream"; + meta = with pkgs.lib.maintainers; { + maintainers = [ jwygoda ]; + }; + + machine = + { pkgs, ... }: + { services.litestream = { + enable = true; + settings = { + dbs = [ + { + path = "/var/lib/grafana/data/grafana.db"; + replicas = [{ + url = "sftp://foo:bar@127.0.0.1:22/home/foo/grafana"; + }]; + } + ]; + }; + }; + systemd.services.grafana.serviceConfig.ExecStartPost = "+" + pkgs.writeShellScript "grant-grafana-permissions" '' + timeout=10 + + while [ ! -f /var/lib/grafana/data/grafana.db ]; + do + if [ "$timeout" == 0 ]; then + echo "ERROR: Timeout while waiting for /var/lib/grafana/data/grafana.db." + exit 1 + fi + + sleep 1 + + ((timeout--)) + done + + find /var/lib/grafana -type d -exec chmod -v 775 {} \; + find /var/lib/grafana -type f -exec chmod -v 660 {} \; + ''; + services.openssh = { + enable = true; + allowSFTP = true; + listenAddresses = [ { addr = "127.0.0.1"; port = 22; } ]; + }; + services.grafana = { + enable = true; + security = { + adminUser = "admin"; + adminPassword = "admin"; + }; + addr = "localhost"; + port = 3000; + extraOptions = { + DATABASE_URL = "sqlite3:///var/lib/grafana/data/grafana.db?cache=private&mode=rwc&_journal_mode=WAL"; + }; + }; + users.users.foo = { + isNormalUser = true; + password = "bar"; + }; + users.users.litestream.extraGroups = [ "grafana" ]; + }; + + testScript = '' + start_all() + machine.wait_until_succeeds("test -d /home/foo/grafana") + machine.wait_for_open_port(3000) + machine.succeed(""" + curl -sSfN -X PUT -H "Content-Type: application/json" -d '{ + "oldPassword": "admin", + "newPassword": "newpass", + "confirmNew": "newpass" + }' http://admin:admin@127.0.0.1:3000/api/user/password + """) + # https://litestream.io/guides/systemd/#simulating-a-disaster + machine.systemctl("stop litestream.service") + machine.succeed( + "rm -f /var/lib/grafana/data/grafana.db " + "/var/lib/grafana/data/grafana.db-shm " + "/var/lib/grafana/data/grafana.db-wal" + ) + machine.succeed( + "litestream restore /var/lib/grafana/data/grafana.db " + "&& chown grafana:grafana /var/lib/grafana/data/grafana.db " + "&& chmod 660 /var/lib/grafana/data/grafana.db" + ) + machine.systemctl("restart grafana.service") + machine.wait_for_open_port(3000) + machine.succeed( + "curl -sSfN -u admin:newpass http://127.0.0.1:3000/api/org/users | grep admin\@localhost" + ) + ''; +}) diff --git a/nixos/tests/mpv.nix b/nixos/tests/mpv.nix new file mode 100644 index 00000000000..bcfc17cf332 --- /dev/null +++ b/nixos/tests/mpv.nix @@ -0,0 +1,28 @@ +import ./make-test-python.nix ({ lib, ... }: + +with lib; + +let + port = toString 4321; +in +{ + name = "mpv"; + meta.maintainers = with maintainers; [ zopieux ]; + + nodes.machine = + { pkgs, ... }: + { + environment.systemPackages = [ + pkgs.curl + (pkgs.mpv-with-scripts.override { + scripts = [ pkgs.mpvScripts.simple-mpv-webui ]; + }) + ]; + }; + + testScript = '' + machine.execute("set -m; mpv --script-opts=webui-port=${port} --idle=yes &") + machine.wait_for_open_port(${port}) + assert "<title>simple-mpv-webui" in machine.succeed("curl -s localhost:${port}") + ''; +}) diff --git a/nixos/tests/node-red.nix b/nixos/tests/node-red.nix new file mode 100644 index 00000000000..7660bc32f4c --- /dev/null +++ b/nixos/tests/node-red.nix @@ -0,0 +1,31 @@ +import ./make-test-python.nix ({ pkgs, ... }: { + name = "nodered"; + meta = with pkgs.lib.maintainers; { + maintainers = [ matthewcroughan ]; + }; + + nodes = { + client = { config, pkgs, ... }: { + environment.systemPackages = [ pkgs.curl ]; + }; + nodered = { config, pkgs, ... }: { + services.node-red = { + enable = true; + openFirewall = true; + }; + }; + }; + + testScript = '' + start_all() + nodered.wait_for_unit("node-red.service") + nodered.wait_for_open_port("1880") + + client.wait_for_unit("multi-user.target") + + with subtest("Check that the Node-RED webserver can be reached."): + assert "<title>Node-RED</title>" in client.succeed( + "curl -sSf http:/nodered:1880/ | grep title" + ) + ''; +}) diff --git a/nixos/tests/prometheus-exporters.nix b/nixos/tests/prometheus-exporters.nix index e8bc6339ecf..6153ba9e366 100644 --- a/nixos/tests/prometheus-exporters.nix +++ b/nixos/tests/prometheus-exporters.nix @@ -273,6 +273,26 @@ let ''; }; + influxdb = { + exporterConfig = { + enable = true; + sampleExpiry = "3s"; + }; + exporterTest = '' + wait_for_unit("prometheus-influxdb-exporter.service") + succeed( + "curl -XPOST http://localhost:9122/write --data-binary 'influxdb_exporter,distro=nixos,added_in=21.09 value=1'" + ) + succeed( + "curl -sSf http://localhost:9122/metrics | grep 'nixos'" + ) + execute("sleep 5") + fail( + "curl -sSf http://localhost:9122/metrics | grep 'nixos'" + ) + ''; + }; + jitsi = { exporterConfig = { enable = true; diff --git a/nixos/tests/rabbitmq.nix b/nixos/tests/rabbitmq.nix index 8a7fcc0e899..69be29b0f9e 100644 --- a/nixos/tests/rabbitmq.nix +++ b/nixos/tests/rabbitmq.nix @@ -8,6 +8,8 @@ import ./make-test-python.nix ({ pkgs, ... }: { machine = { services.rabbitmq.enable = true; + # Ensure there is sufficient extra disk space for rabbitmq to be happy + virtualisation.diskSize = 1024; }; testScript = '' diff --git a/nixos/tests/soapui.nix b/nixos/tests/soapui.nix new file mode 100644 index 00000000000..205128df91f --- /dev/null +++ b/nixos/tests/soapui.nix @@ -0,0 +1,24 @@ +import ./make-test-python.nix ({ pkgs, ... }: { + name = "soapui"; + meta = with pkgs.lib.maintainers; { + maintainers = [ asbachb ]; + }; + + machine = { config, pkgs, ... }: { + imports = [ + ./common/x11.nix + ]; + + services.xserver.enable = true; + + environment.systemPackages = [ pkgs.soapui ]; + }; + + testScript = '' + machine.wait_for_x() + machine.succeed("soapui &") + machine.wait_for_window(r"SoapUI \d+\.\d+\.\d+") + machine.sleep(1) + machine.screenshot("soapui") + ''; +}) diff --git a/nixos/tests/syncthing-init.nix b/nixos/tests/syncthing-init.nix index 4581e3fd4fb..8b60ad7faf0 100644 --- a/nixos/tests/syncthing-init.nix +++ b/nixos/tests/syncthing-init.nix @@ -9,15 +9,14 @@ in { machine = { services.syncthing = { enable = true; - declarative = { - devices.testDevice = { - id = testId; - }; - folders.testFolder = { - path = "/tmp/test"; - devices = [ "testDevice" ]; - }; + devices.testDevice = { + id = testId; }; + folders.testFolder = { + path = "/tmp/test"; + devices = [ "testDevice" ]; + }; + extraOptions.gui.user = "guiUser"; }; }; @@ -27,5 +26,6 @@ in { assert "testFolder" in config assert "${testId}" in config + assert "guiUser" in config ''; }) diff --git a/nixos/tests/syncthing.nix b/nixos/tests/syncthing.nix index 5536b7055cc..aff1d874413 100644 --- a/nixos/tests/syncthing.nix +++ b/nixos/tests/syncthing.nix @@ -25,7 +25,7 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: { "xmllint --xpath 'string(configuration/gui/apikey)' %s/config.xml" % confdir ).strip() oldConf = host.succeed( - "curl -Ssf -H 'X-API-Key: %s' 127.0.0.1:8384/rest/system/config" % APIKey + "curl -Ssf -H 'X-API-Key: %s' 127.0.0.1:8384/rest/config" % APIKey ) conf = json.loads(oldConf) conf["devices"].append({"deviceID": deviceID, "id": name}) @@ -39,7 +39,7 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: { ) newConf = json.dumps(conf) host.succeed( - "curl -Ssf -H 'X-API-Key: %s' 127.0.0.1:8384/rest/system/config -d %s" + "curl -Ssf -H 'X-API-Key: %s' 127.0.0.1:8384/rest/config -X PUT -d %s" % (APIKey, shlex.quote(newConf)) ) |